繁体   English   中英

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

[英]Create multiple threads and wait for all of them to complete

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

这取决于您使用的 .NET Framework 版本。 .NET 4.0 使用 Tasks 使线程管理变得更加容易:

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");
}

我认为你需要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);

我做了一个非常简单的扩展方法来等待集合的所有线程:

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();

在 .NET 4.0 中,您可以使用任务并行库

在早期版本中,您可以在循环中创建Thread对象列表,对每个对象调用Start ,然后进行另一个循环并在每个对象上调用Join

如果您不想使用 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();
}

就我而言,我无法使用Task.Run()Task.Factory.StartNew()在线程池上实例化我的对象。 他们不会正确同步我长时间运行的代表。

我需要委托异步运行,暂停我的主线程以使其集体完成。 Thread.Join()不起作用,因为我想在父线程中间等待集体完成,而不是在最后。

使用Task.Run()Task.Factory.StartNew() ,所有子线程相互阻塞或父线程不会被阻塞,......我无法弄清楚如何使用async委托,因为await语法的重新序列化。

这是我使用线程而不是任务的解决方案:

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

我不知道是否有更好的方法,但下面描述了我是如何使用计数器和后台工作线程来实现的。

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.
}

大多数建议的答案都没有考虑超时间隔,这对于防止可能的死锁非常重要。 接下来是我的示例代码。 (请注意,我主要是一名 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");
    }
}

您可以创建多个线程并将它们定位到这样的函数。 在这个例子中有 10 个线程。

static void do_request() {
    Console.WriteLine("hi");
}

for (int i = 0; i < 10; i++) {
    Thread t = new Thread(do_request);
    t.Start();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM