如何创建多个线程并等待所有线程完成?
答案 0 :(得分:96)
这取决于您使用的.NET Framework版本。 .NET 4.0使用任务使线程管理变得更加容易:
class Program
{
static void Main(string[] args)
{
Task task1 = Task.Factory.StartNew(() => doStuff());
Task task2 = Task.Factory.StartNew(() => doStuff());
Task task3 = Task.Factory.StartNew(() => doStuff());
Task.WaitAll(task1, task2, task3);
Console.WriteLine("All threads complete");
}
static void doStuff()
{
//do stuff here
}
}
在以前的.NET版本中,您可以使用BackgroundWorker
对象,使用ThreadPool.QueueUserWorkItem()
,或手动创建线程并使用Thread.Join()
等待它们完成:
static void Main(string[] args)
{
Thread t1 = new Thread(doStuff);
t1.Start();
Thread t2 = new Thread(doStuff);
t2.Start();
Thread t3 = new Thread(doStuff);
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("All threads complete");
}
答案 1 :(得分:26)
我认为你需要WaitHandler.WaitAll。这是一个例子:
public static void Main(string[] args)
{
int numOfThreads = 10;
WaitHandle[] waitHandles = new WaitHandle[numOfThreads];
for (int i = 0; i < numOfThreads; i++)
{
var j = i;
// Or you can use AutoResetEvent/ManualResetEvent
var handle = new EventWaitHandle(false, EventResetMode.ManualReset);
var thread = new Thread(() =>
{
Thread.Sleep(j * 1000);
Console.WriteLine("Thread{0} exits", j);
handle.Set();
});
waitHandles[j] = handle;
thread.Start();
}
WaitHandle.WaitAll(waitHandles);
Console.WriteLine("Main thread exits");
Console.Read();
}
编辑 FCL有一些更方便的功能。
(1)Task.WaitAll,以及它的重载,当你想并行执行某些任务时(并且没有返回值)。
var tasks = new[]
{
Task.Factory.StartNew(() => DoSomething1()),
Task.Factory.StartNew(() => DoSomething2()),
Task.Factory.StartNew(() => DoSomething3())
};
Task.WaitAll(tasks);
(2)Task.WhenAll当您想要使用返回值执行某些任务时,它会执行操作并将结果放入数组中。它是线程安全的,您不需要使用线程安全的容器并自己实现添加操作。
var tasks = new[]
{
Task.Factory.StartNew(() => GetSomething1()),
Task.Factory.StartNew(() => GetSomething2()),
Task.Factory.StartNew(() => GetSomething3())
};
var things = Task.WhenAll(tasks);
答案 2 :(得分:6)
我做了一个非常简单的扩展方法来等待集合的所有线程:
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();
答案 3 :(得分:5)
在.Net 4.0中,您可以使用Task Parallel Library。
在早期版本中,您可以在循环中创建Thread
个对象的列表,在每个对象上调用Start
,然后再创建一个循环并在每个循环上调用Join
。
答案 4 :(得分:1)
如果您不想使用Task类(例如在.NET 3.5中),您可以启动所有线程,然后将它们添加到列表中并将它们连接到foreach循环中。
示例:
List<Thread> threads = new List<Thread>();
// Start threads
for(int i = 0; i<10; i++)
{
int tmp = i; // copy value for closure
Thread t = new Thread(() => Console.WriteLine(tmp));
t.Start;
threads.Add(t);
}
// Await threads
foreach(Thread thread in threads)
{
thread.Join();
}
答案 5 :(得分:0)
不知道是否有更好的方法,但下面介绍了我如何使用计数器和后台工作程序线程。
private object _lock=new object();
private int _runningThreads = 0;
private int Counter{
get{
lock(_lock)
return _runningThreads;
}
set{
lock(_lock)
_runningThreads = value;
}
}
现在,无论何时创建工作线程,都要递增计数器:
var t=new BackgroundWorker();
//ADD RunWorkerCompleted HANDLER
//START THREAD
Counter++;
在完成的工作中,减少计数器:
private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Counter--;
}
现在您可以随时检查计数器以查看是否有任何线程正在运行:
if(Couonter>0){
//SOME THREAD IS YET TO FINISH.
}
答案 6 :(得分:0)
大多数建议的答案没有考虑超时间隔,这对于防止可能的死锁非常重要。接下来是我的示例代码。 (请注意,我主要是Win32开发人员,我就是这样做的。)
//'arrRunningThreads' = List<Thread>
//Wait for all threads
const int knmsMaxWait = 3 * 1000; //3 sec timeout
int nmsBeginTicks = Environment.TickCount;
foreach(Thread thrd in arrRunningThreads)
{
//See time left
int nmsElapsed = Environment.TickCount - nmsBeginTicks;
int nmsRemain = knmsMaxWait - nmsElapsed;
if(nmsRemain < 0)
nmsRemain = 0;
//Then wait for thread to exit
if(!thrd.Join(nmsRemain))
{
//It didn't exit in time, terminate it
thrd.Abort();
//Issue a debugger warning
Debug.Assert(false, "Terminated thread");
}
}
答案 7 :(得分:0)
在我的情况下,我无法使用Task.Run()
或Task.Factory.StartNew()
在线程池上实例化我的对象。他们不会正确地同步我长时间运行的代表。我需要委托以异步方式运行,暂停我的主线程以便集体完成。 Thread.Join()
不起作用,因为我想在父线程的中间等待集体完成,而不是在最后。使用Task.Run()
或Task.Factory.StartNew()
所有子线程相互阻塞或父线程不会被阻止,...我无法弄清楚如何使用async
委托,因为await
语法的重新序列化。
以下是使用Threads而不是Tasks的解决方案:
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.ManualReset))
{
int outdex = mediaServerMinConnections - 1;
for (int i = 0; i < mediaServerMinConnections; i++)
{
new Thread(() =>
{
sshPool.Enqueue(new SshHandler());
if (Interlocked.Decrement(ref outdex) < 1)
wh.Set();
}).Start();
}
wh.WaitOne();
}
(密集编码风格的道歉。)