【问题标题】:Wait until all threads finished their work in ThreadPool等到所有线程完成它们在 ThreadPool 中的工作
【发布时间】:2011-01-31 23:42:01
【问题描述】:

我有这个代码:

var list = new List<int>();
for(int i=0;i<10;i++) list.Add(i); 
for(int i=0;i<10;i++)
{
     ThreadPool.QueueUserWorkItem(
         new WaitCallback(x => {
             Console.WriteLine(x);  
         }), list[i]);
} 

我想知道所有线程池线程何时完成工作。我该怎么做?

【问题讨论】:

    标签: c# threadpool


    【解决方案1】:

    您需要自己跟踪。

    一种选择是使用计数器和重置事件:

    int toProcess = 10;
    using(ManualResetEvent resetEvent = new ManualResetEvent(false))
    {
        var list = new List<int>();
        for(int i=0;i<10;i++) list.Add(i); 
        for(int i=0;i<10;i++)
        {
            ThreadPool.QueueUserWorkItem(
               new WaitCallback(x => {
                  Console.WriteLine(x);  
                  // Safely decrement the counter
                  if (Interlocked.Decrement(ref toProcess)==0)
                     resetEvent.Set();
    
               }),list[i]);
        } 
    
        resetEvent.WaitOne();
    }
    // When the code reaches here, the 10 threads will be done
    Console.WriteLine("Done");
    

    【讨论】:

    • 别忘了释放重置事件 - 非托管资源。
    • @Aaronaught:为你添加了这个;)
    • 为什么你使用 Interlocked.Decrement(ref toProcess) insted of --toProcess?
    • @Neir0:由于许多线程可能同时执行此操作,因此您需要保护该减量。有两种选择 - 使用互锁,或使用某种形式的锁定。如果您只使用 --toProcess,并且两个线程尝试同时执行此操作,那么您永远不会达到零。
    • 读者要注意if(Interlocked.Decrement(...))这一行特别重要,不要试图只做if(someVarDecrementedOnAnotherThread == 0),因为c#编译器有权优化掉不能为真的条件,从当前线程的角度来看——即使在其他地方该变量已被Interlocked.Decrement() 递减。通常,请始终记住,在编写无锁代码时,CPU(缓存一致性)和编译器(不变量删除)都可能会尝试进行优化。不错的答案,顺便说一句。
    【解决方案2】:

    在 .NET Framework 4+ 中使用方便的 System.Threading.CountdownEvent 类:

    const int threadCount = 10;
    var list = new List<int>(threadCount);
    for (var i = 0; i < threadCount; i++) list.Add(i);
    
    using (var countdownEvent = new CountdownEvent(threadCount))
    {
        for (var i = 0; i < threadCount; i++)
            ThreadPool.QueueUserWorkItem(
                x =>
                {
                    Console.WriteLine(x);
                    countdownEvent.Signal();
                }, list[i]);
    
        countdownEvent.Wait();
    }
    Console.WriteLine("done");
    

    【讨论】:

    • 非常干净,不像其他解决方案那样冗长,我很惊讶这没有得到那么多的支持。
    【解决方案3】:

    我不确定 ThreadPool 是否公开了此类功能,但您可以使用等待句柄,顺便说一下,似乎没有必要重复两次:

    var events = new ManualResetEvent[10];
    var list = new List<int>();
    for (int i = 0; i < 10; i++)
    {
        list.Add(i);
        events[i] = new ManualResetEvent(false);
        int j = i;
        ThreadPool.QueueUserWorkItem(x => {
            Console.WriteLine(x);
            events[j].Set();
        }, list[i]);
    }
    WaitHandle.WaitAll(events);
    

    【讨论】:

    • +1 - 与我发布的解决方案相同;只有你更快,代码更优雅。
    • 好吧,你也比我快。
    • 只是一个警告...如果您在 STA 线程中有超过 64 个项目,WaitHandle.WaitAll 将失败...
    • 当您只需要 1 个事件时,为什么还要创建 10 个事件?
    • @Pandiya Chendur,请停止在与此处讨论的主题完全无关的线程上发布有关您的问题的 cmets。我看到了您的问题和给出的答案(尤其是关于使用 jqGrid 的答案),我认为这是一个很好的答案,这就是我没有回复您的原因。也不要发布重复的问题,这不会给你更多/更好的答案。
    【解决方案4】:

    这就是我的做法。

    class Program
    {
        static void Main(string[] args)
        {
            var items = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            using (var countdown = new Countdown(items.Length))
            {
                foreach (var item in items)
                {
                    ThreadPool.QueueUserWorkItem(o =>
                    {
                        Thread.SpinWait(100000000);
                        Console.WriteLine("Thread Done!");
                        countdown.Signal();
                    });
                }
                countdown.Wait();
            }
            Console.WriteLine("Job Done!");
            Console.ReadKey();
        }
    
        public class Countdown : IDisposable
        {
            private readonly ManualResetEvent done;
            private readonly int total;
            private volatile int current;
    
            public Countdown(int total)
            {
                this.total = total;
                current = total;
                done = new ManualResetEvent(false);
            }
    
            public void Signal()
            {
                lock (done)
                {
                    if (current > 0 && --current == 0)
                        done.Set();
                }
            }
    
            public void Wait()
            {
                done.WaitOne();
            }
    
            public void Dispose()
            {
                done.Dispose();
            }
        }
    } 
    

    【讨论】:

    • 使用 Interlocked.Decrement 而不是锁会更高效。相关代码见我的回答。
    • 在倒计时中调用 done.Dispose() 不正确,给出“由于其保护级别而无法访问”。应该这样做。Close() 因为 Close 和 Dispose 是等价的。
    【解决方案5】:

    线程池不会告诉你线程何时完成执行,因此工作项必须自己完成。我改变了这样的代码:

        var list = new List<int>();
        ManualResetEvent[] handles = new ManualResetEvent[10];
        for (int i = 0; i < 10; i++) {
            list.Add(i);
            handles[i] = new ManualResetEvent(false);
        }
        for (int i = 0; i < 10; i++) {
            ThreadPool.QueueUserWorkItem(
             new WaitCallback(x =>
             {
                 Console.WriteLine(x);
                 handles[(int) x].Set();
             }), list[i]);
        }
    
        WaitHandle.WaitAll(handles);
    

    【讨论】:

      【解决方案6】:
          static void Main(string[] args)
          {
              for (int i = 0; i < 10; i++)
              {
                  ThreadPool.QueueUserWorkItem(new WaitCallback(xyz));
              }
      
              bool working = true;
              ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
              while (working)
              {
                  ThreadPool.GetAvailableThreads(out int workerThreads, out int completionPortThreads);
                  //Console.WriteLine($"{workerThreads} , {maxWorkerThreads}");
                  if (workerThreads == maxWorkerThreads)
                  { working = false; }
              }
              //when all threads are completed then 'working' will be false 
          }
          void xyz(object o)
          {
              console.writeline("");
          }
      

      【讨论】:

        猜你喜欢
        • 2011-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多