代码之家  ›  专栏  ›  技术社区  ›  JSBÕ±Õ¸Õ£Õ¹

C#:等待所有线程完成

  •  59
  • JSBÕ±Õ¸Õ£Õ¹  · 技术社区  · 17 年前

    我在编写的代码中遇到了一个常见的模式,我需要等待组中的所有线程完成,并超时。超时时间应该是 所有 要完成线程,只需执行以下操作 Thread.Join(timeout) 因为每个线程都不起作用,因为可能的超时时间是 timeout * numThreads .

    现在我做如下事情:

    var threadFinishEvents = new List<EventWaitHandle>();
    
    foreach (DataObject data in dataList)
    {
        // Create local variables for the thread delegate
        var threadFinish = new EventWaitHandle(false, EventResetMode.ManualReset);
        threadFinishEvents.Add(threadFinish);
    
        var localData = (DataObject) data.Clone();
        var thread = new Thread(
            delegate()
            {
                DoThreadStuff(localData);
                threadFinish.Set();
            }
        );
        thread.Start();
    }
    
    Mutex.WaitAll(threadFinishEvents.ToArray(), timeout);
    

    然而,对于这类事情似乎应该有一个更简单的习语。

    9 回复  |  直到 17 年前
        1
  •  27
  •   Martin v. Löwis    17 年前

    我仍然认为使用Join更简单。记录预期的完成时间(如Now+timeout),然后在循环中执行

    if(!thread.Join(End-now))
        throw new NotFinishedInTime();
    
        2
  •  23
  •   T. Webster    14 年前

    和。NET 4.0我发现 System.Threading.Tasks 工作起来容易多了。这是一个对我来说可靠的自旋等待循环。它阻塞主线程,直到所有任务完成。还有 Task.WaitAll 但这对我来说并不总是奏效。

            for (int i = 0; i < N; i++)
            {
                tasks[i] = Task.Factory.StartNew(() =>
                {               
                     DoThreadStuff(localData);
                });
            }
            while (tasks.Any(t => !t.IsCompleted)) { } //spin wait
    
        3
  •  9
  •   Omer van Kloeten    17 年前

    这并没有回答问题(没有超时),但我做了一个非常简单的扩展方法来等待集合的所有线程:

    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();
    
        4
  •  9
  •   Brian Gideon    15 年前

    既然这个问题被搁置了,我会继续发布我的解决方案。

    using (var finished = new CountdownEvent(1)) 
    { 
      for (DataObject data in dataList) 
      {   
        finished.AddCount();
        var localData = (DataObject)data.Clone(); 
        var thread = new Thread( 
            delegate() 
            {
              try
              {
                DoThreadStuff(localData); 
                threadFinish.Set();
              }
              finally
              {
                finished.Signal();
              }
            } 
        ); 
        thread.Start(); 
      }  
      finished.Signal(); 
      finished.Wait(YOUR_TIMEOUT); 
    } 
    
        5
  •  8
  •   Vincent    11 年前

    我简直想不通,你为什么不直接穿呢。加入(超时)并从总超时时间中删除加入所花费的时间?

    // pseudo-c#:
    
    TimeSpan timeout = timeoutPerThread * threads.Count();
    
    foreach (Thread thread in threads)
    {
        DateTime start = DateTime.Now;
    
        if (!thread.Join(timeout))
            throw new TimeoutException();
    
        timeout -= (DateTime.Now - start);
    }
    

    编辑: 代码现在不那么伪了。不明白为什么你会把答案改为-2,而你改为+4的答案完全相同,只是不那么详细。

        6
  •  7
  •   Jon Norton    17 年前

    这可能不是您的选择,但如果您可以使用并行扩展。NET,然后您可以使用 Task s而不是原始线程,然后使用 Task.WaitAll() 等待他们完成。

        7
  •  1
  •   mauris    15 年前

    我读过《C#4.0:Herbert Schildt的完整参考》一书。作者使用join给出了一个解决方案:

    class MyThread
        {
            public int Count;
            public Thread Thrd;
            public MyThread(string name)
            {
                Count = 0;
                Thrd = new Thread(this.Run);
                Thrd.Name = name;
                Thrd.Start();
            }
            // Entry point of thread.
            void Run()
            {
                Console.WriteLine(Thrd.Name + " starting.");
                do
                {
                    Thread.Sleep(500);
                    Console.WriteLine("In " + Thrd.Name +
                    ", Count is " + Count);
                    Count++;
                } while (Count < 10);
                Console.WriteLine(Thrd.Name + " terminating.");
            }
        }
        // Use Join() to wait for threads to end.
        class JoinThreads
        {
            static void Main()
            {
                Console.WriteLine("Main thread starting.");
                // Construct three threads.
                MyThread mt1 = new MyThread("Child #1");
                MyThread mt2 = new MyThread("Child #2");
                MyThread mt3 = new MyThread("Child #3");
                mt1.Thrd.Join();
                Console.WriteLine("Child #1 joined.");
                mt2.Thrd.Join();
                Console.WriteLine("Child #2 joined.");
                mt3.Thrd.Join();
                Console.WriteLine("Child #3 joined.");
                Console.WriteLine("Main thread ending.");
                Console.ReadKey();
            }
        }
    
        8
  •  1
  •   Bùi Công Giao    12 年前

    我试图弄清楚如何做到这一点,但我无法从谷歌得到任何答案。 我知道这是一个老话题,但这是我的解决方案:

    使用以下类:

    class ThreadWaiter
        {
            private int _numThreads = 0;
            private int _spinTime;
    
            public ThreadWaiter(int SpinTime)
            {
                this._spinTime = SpinTime;
            }
    
            public void AddThreads(int numThreads)
            {
                _numThreads += numThreads;
            }
    
            public void RemoveThread()
            {
                if (_numThreads > 0)
                {
                    _numThreads--;
                }
            }
    
            public void Wait()
            {
                while (_numThreads != 0)
                {
                    System.Threading.Thread.Sleep(_spinTime);
                }
            }
        }
    
    1. 在执行线程之前调用Addthreads(int-numThreads)。
    2. 在每个线程完成后调用RemoveThread()。
    3. 在要等待所有线程完成的点使用Wait() 继续之前
        9
  •  0
  •   Alex Aza    13 年前

    可能的解决方案:

    var tasks = dataList
        .Select(data => Task.Factory.StartNew(arg => DoThreadStuff(data), TaskContinuationOptions.LongRunning | TaskContinuationOptions.PreferFairness))
        .ToArray();
    
    var timeout = TimeSpan.FromMinutes(1);
    Task.WaitAll(tasks, timeout);
    

    假设dataList是项目列表,每个项目都需要在单独的线程中处理。