【问题标题】:how to wait for all background threads to finish (in C#)?如何等待所有后台线程完成(在 C# 中)?
【发布时间】:2011-04-24 20:36:04
【问题描述】:

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

【问题讨论】:

标签: c# multithreading


【解决方案1】:

创建一个结构来跟踪您的工作线程

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

完成

【讨论】:

    【解决方案2】:

    这是不完整的代码,但 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());
    

    【讨论】:

    【解决方案3】:

    使用动态数据,您可以传递您的对象和 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();
    }
    

    【讨论】:

      【解决方案4】:

      考虑使用线程池。你想要的大部分已经完成了。有一个example from Microsoft 几乎可以完成您的全部任务。将“fibonacci”替换为“数据库任务”,这听起来像是你的问题。

      【讨论】:

      • 几分钟前我读了这篇文章,正要回复说这是我正在寻找的解决方案。感谢您的回复。
      【解决方案5】:

      您可以将每个启动的线程存储在一个数组中。然后当你需要全部等待时,在循环中对数组中的每个线程调用 Join 方法。

      Thread child = new Thread(...);
      Threads.Add(child);
      child.Start()
      
      ...
      
      foreach(Thread t in Threads)
      {
         t.Join();
      }
      

      HTH

      【讨论】:

      • 感谢您的回复。我认为 thread.Join 方法适用于单个线程或很少固定数量的线程。对于多线程,我认为有一个 WaitAll 方法,但我找不到一个好的代码示例。
      • @RKP:等待所有线程完成与依次等待它们是一样的,不是吗?有一个winapi函数WaitForMultipleObjects,但不是C#,虽然可以用,但我看没有意义。
      猜你喜欢
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多