创建多个线程并等待所有线程完成

时间:2010-11-16 03:23:47

标签: c# .net

如何创建多个线程并等待所有线程完成?

8 个答案:

答案 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();
}

(密集编码风格的道歉。)