如何等待所有后台线程完成(C#)?

5
如何在C#中使前台线程等待所有后台(子)线程完成?我需要从队列(数据库)获取待处理作业列表,启动新线程来执行每个作业,最终等待所有子线程完成。如何在C#中实现这一点?谢谢。

2
可能是[C#生成多个线程进行工作,然后等待所有线程完成]的重复问题。(https://dev59.com/pnE85IYBdhLWcg3w9odJ) - Adam Lear
你尝试过使用EventWaitHandle();吗? - Bonshington
感谢大家对我的问题的快速回复。 - RKP
5个回答

7
您可以将每个启动的线程存储在一个数组中。然后,在需要等待它们全部完成时,使用循环调用数组中每个线程的Join方法。
Thread child = new Thread(...);
Threads.Add(child);
child.Start()

...

foreach(Thread t in Threads)
{
   t.Join();
}

HTH


感谢您的回复。我认为thread.Join()方法适用于单个线程或非常少量的固定数量线程。对于多个线程,我想有一个WaitAll()方法,但我找不到一个好的代码示例。 - RKP
3
等待所有线程完成的意思就是按顺序等待它们完成,对吗?有一个WinAPI函数WaitForMultipleObjects,但它不是C#,虽然你可以使用它,但我看不出有什么意义。 - Armen Tsirunyan

2

考虑使用线程池。大部分你想要的功能已经实现了。微软有一个示例,几乎可以完成你的整个任务。将“斐波那契”替换为“数据库任务”,听起来就像是你的问题。


我几分钟前读了这篇文章,正要回复说这就是我正在寻找的解决方案。谢谢你的回复。 - RKP
以上示例来自微软的网页存档,该文档于2015年被下线之前进行了最后一次复制。该示例已经移动到较新版本中的ThreadPool.QueueUserWorkItem方法的文档中。 - LeBleu

1

使用动态数据,您可以传递对象和WaitHandle(ActionResetEvent),让您等待所有后台线程完成,无需声明额外的类:

static void Main(string[] args)
{
    List<AutoResetEvent> areList = new List<AutoResetEvent>();
    foreach (MyObject o in ListOfMyObjects)
    {
        AutoResetEvent are = new AutoResetEvent(false);
        areList.Add(are);
        ThreadPool.QueueUserWorkItem(DoWork, new { o, are });
    };

    Console.WriteLine("Time: {0}", DateTime.Now);
    WaitHandle.WaitAll(areList.ToArray());
    Console.WriteLine("Time: {0}", DateTime.Now);
    Console.ReadKey();
}

static void DoWork(object state)
{
    dynamic o = state;
    MyObject myObject = (MyObject)o.o;
    AutoResetEvent are = (AutoResetEvent)o.are;

    myObject.Execute();
    are.Set();
}

1

这是不完整的代码,但 ManualResetEvent 对你有用。

var waitEvents = new List<ManualResetEvent>();
foreach (var action in actions)
{
    var evt = new ManualResetEvent(false);
    waitEvents.Add(evt);
    ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, TimeoutCallback, state, 5000, true);
}

if (waitEvents.Count > 0)
    WaitHandle.WaitAll(waitEvents.ToArray());

谢谢,但是在这段代码中每个操作的新线程从哪里开始?“5000”是超时设置吗? - RKP
请查看完整代码,请注意,我只是从以前的一个答案中复制了一小部分:http://stackoverflow.com/questions/3915017/image-url-validation-in-c/3915440#3915440 - danijels
是的,5000是一个超时设置。 - danijels

0
创建一个结构来跟踪您的工作线程。
private struct WorkerThreadElement
{
    public IAsyncResult WorkerThreadResult;
    public AsyncActionExecution WorkerThread;
}

您还需要跟踪预计创建的线程总数以及当前已完成的线程数量

private int _TotalThreads = 0;
private int _ThreadsHandled = 0;
private List<WorkerThreadElement> _WorkerThreadElements = new List<WorkerThreadElement>();

然后创建一个自动重置句柄以等待线程完成。

// The wait handle thread construct to signal the completion of this process
private EventWaitHandle _CompletedHandle = new AutoResetEvent(false);

你还需要一个委托来创建新的线程 - 有多种方法可以做到这一点,但我选择了一个简单的委托来举例说明。

// Delegate to asynchronously invoke an action
private delegate void AsyncActionExecution();

假设Invoke方法是创建所有线程并等待它们执行的入口点。因此我们有:
public void Invoke()
{ 
    _TotalThreads = N; /* Change with the total number of threads expected */

    foreach (Object o in objects) 
    {
        this.InvokeOneThread();
    }             

    // Wait until execution has been completed
    _CompletedHandle.WaitOne();

    // Collect any exceptions thrown and bubble them up
    foreach (WorkerThreadElement workerThreadElement in _WorkerThreadElements)
    {
        workerThreadElement.WorkerThread.EndInvoke(workerThreadElement.WorkerThreadResult);
    }
}         

InvokeOneThread是用于创建单个操作线程的方法。在这里,我们需要创建一个工作线程元素并调用实际线程。

 private void InvokeOneThread()
 {
     WorkerThreadElement threadElement = new WorkerThreadElement();
     threadElement.WorkerThread = new AsyncActionExecution();
     threadElement.WorkerThreadResult = threadElement.WorkerThread.BeginInvoke(actionParameters, InvokationCompleted, null);

     _WorkerThreadElements.Add(threadElement);
 }

线程完成的回调

private object _RowLocker = new object();

/// <summary>
/// Increment the number of rows that have been fully processed
/// </summary>
/// <param name="ar"></param>
private void InvokationCompleted(IAsyncResult ar)
{
    lock (_RowLocker) 
    {
        _RowsHandled++; 
    }

    if (_TotalThreads == _ThreadsHandled) 
        _CompletedHandle.Set();
}

完成


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接