Ответ 1
Я все еще думаю, что использование Join проще. Запишите ожидаемое время завершения (как время + время ожидания), затем в цикле выполните
if(!thread.Join(End-now))
throw new NotFinishedInTime();
Я использую общий шаблон в коде, который я пишу, где мне нужно дождаться завершения всех потоков в группе с тайм-аутом. Тайм-аут должен быть временем, требуемым для завершения всех потоков, поэтому простое выполнение thread.Join(timeout) для каждого потока не будет работать, так как возможный тайм-аут - это таймаут * numThreads.
Сейчас я делаю что-то вроде следующего:
var threadFinishEvents = new List<EventWaitHandle>();
foreach (DataObject data in dataList)
{
// Create local variables for the thread delegate
var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
threadFinishEvents.Add(threadFinish);
var localData = (DataObject) data.Clone();
var thread = new Thread(
delegate()
{
DoThreadStuff(localData);
threadFinish.Set();
}
);
thread.Start();
}
Mutex.WaitAll(threadFinishEvents.ToArray(), timeout);
Однако, похоже, для такого рода вещей должна быть более простая идиома.
Я все еще думаю, что использование Join проще. Запишите ожидаемое время завершения (как время + время ожидания), затем в цикле выполните
if(!thread.Join(End-now))
throw new NotFinishedInTime();
С .NET 4.0 я нахожу System.Threading.Tasks намного легче работать. Здесь цикл spin-wait, который работает надежно для меня. Он блокирует основной поток, пока все задачи не завершатся. Там также Task.WaitAll, но это не всегда срабатывало для меня.
for (int i = 0; i < N; i++)
{
tasks[i] = Task.Factory.StartNew(() =>
{
DoThreadStuff(localData);
});
}
while (tasks.Any(t => !t.IsCompleted)) { } //spin wait
Так как вопрос встал, я пойду вперед и отправлю свое решение.
using (var finished = new CountdownEvent(1))
{
for (DataObject data in dataList)
{
finished.AddCount();
var localData = (DataObject)data.Clone();
var thread = new Thread(
delegate()
{
try
{
DoThreadStuff(localData);
threadFinish.Set();
}
finally
{
finished.Signal();
}
}
);
thread.Start();
}
finished.Signal();
finished.Wait(YOUR_TIMEOUT);
}
Сверху моей головы, почему бы вам не просто Thread.Join(timeout) и удалить время, необходимое для соединения с общим таймаутом?
// pseudo-c#:
TimeSpan timeout = timeoutPerThread * threads.Count();
foreach (Thread thread in threads)
{
DateTime start = DateTime.Now;
if (!thread.Join(timeout))
throw new TimeoutException();
timeout -= (DateTime.Now - start);
}
Изменить: код теперь меньше псевдо. не понимаю, почему вы ответили бы на вопрос -2, когда ответ, который вы изменили +4, точно такой же, только менее подробный.
Это не отвечает на вопрос (без таймаута), но я сделал очень простой метод расширения для ожидания всех потоков коллекции:
using System.Collections.Generic;
using System.Threading;
namespace Extensions
{
public static class ThreadExtension
{
public static void WaitAll(this IEnumerable<Thread> threads)
{
if(threads!=null)
{
foreach(Thread thread in threads)
{ thread.Join(); }
}
}
}
}
Затем вы просто вызываете:
List<Thread> threads=new List<Thread>();
//Add your threads to this collection
threads.WaitAll();
Это может быть не для вас, но если вы можете использовать Parallel Extension для .NET, вы можете использовать Task
вместо необработанных потоков, а затем использовать Task.WaitAll()
, чтобы дождаться их завершения.
Я связывался, чтобы выяснить, как это сделать, но я не мог получить ответы от Google. Я знаю, что это старый поток, но вот мое решение:
Используйте следующий класс:
class ThreadWaiter
{
private int _numThreads = 0;
private int _spinTime;
public ThreadWaiter(int SpinTime)
{
this._spinTime = SpinTime;
}
public void AddThreads(int numThreads)
{
_numThreads += numThreads;
}
public void RemoveThread()
{
if (_numThreads > 0)
{
_numThreads--;
}
}
public void Wait()
{
while (_numThreads != 0)
{
System.Threading.Thread.Sleep(_spinTime);
}
}
}
Я прочитал книгу С# 4.0: Полный справочник Герберта Шильдта. Автор использует соединение, чтобы дать решение:
class MyThread
{
public int Count;
public Thread Thrd;
public MyThread(string name)
{
Count = 0;
Thrd = new Thread(this.Run);
Thrd.Name = name;
Thrd.Start();
}
// Entry point of thread.
void Run()
{
Console.WriteLine(Thrd.Name + " starting.");
do
{
Thread.Sleep(500);
Console.WriteLine("In " + Thrd.Name +
", Count is " + Count);
Count++;
} while (Count < 10);
Console.WriteLine(Thrd.Name + " terminating.");
}
}
// Use Join() to wait for threads to end.
class JoinThreads
{
static void Main()
{
Console.WriteLine("Main thread starting.");
// Construct three threads.
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
mt1.Thrd.Join();
Console.WriteLine("Child #1 joined.");
mt2.Thrd.Join();
Console.WriteLine("Child #2 joined.");
mt3.Thrd.Join();
Console.WriteLine("Child #3 joined.");
Console.WriteLine("Main thread ending.");
Console.ReadKey();
}
}
Task.Factory.ContinueWhenAll
Возможное решение:
var tasks = dataList
.Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness))
.ToArray();
var timeout = TimeSpan.FromMinutes(1);
Task.WaitAll(tasks, timeout);
Предполагая, что dataList - это список элементов, и каждый элемент должен обрабатываться в отдельном потоке.