简体   繁体   English

C#:等待所有线程完成

[英]C#: Waiting for all threads to complete

I'm running into a common pattern in the code that I'm writing, where I need to wait for all threads in a group to complete, with a timeout. 我正在编写我正在编写的代码中的常见模式,我需要等待组中的所有线程完成,并且超时。 The timeout is supposed to be the time required for all threads to complete, so simply doing thread.Join(timeout) for each thread won't work, since the possible timeout is then timeout * numThreads. 超时应该是所有线程完成所需的时间,因此简单地为每个线程执行thread.Join(timeout)将不起作用,因为可能的超时是超时* numThreads。

Right now I do something like the following: 现在我做类似以下的事情:

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

However, it seems like there should be a simpler idiom for this sort of thing. 但是,对于这种事情,似乎应该有一个更简单的习语。

I still think using Join is simpler. 我仍然认为使用Join更简单。 Record the expected completion time (as Now+timeout), then, in a loop, do 记录预期的完成时间(如现在+超时),然后在循环中执行

if(!thread.Join(End-now))
    throw new NotFinishedInTime();

With .NET 4.0 I find System.Threading.Tasks a lot easier to work with. 使用.NET 4.0,我发现System.Threading.Tasks更容易使用。 Here's spin-wait loop which works reliably for me. 这是旋转等待循环,对我来说可靠。 It blocks the main thread until all the tasks complete. 它会阻塞主线程,直到完成所有任务。 There's also Task.WaitAll , but that hasn't always worked for me. 还有Task.WaitAll ,但这对我来说并不总是有用

        for (int i = 0; i < N; i++)
        {
            tasks[i] = Task.Factory.StartNew(() =>
            {               
                 DoThreadStuff(localData);
            });
        }
        while (tasks.Any(t => !t.IsCompleted)) { } //spin wait

Since the question got bumped I will go ahead and post my solution. 由于问题得到了解决,我将继续发布我的解决方案。

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

Off the top of my head, why don't you just Thread.Join(timeout) and remove the time it took to join from the total timeout? 在我的脑海中,你为什么不只是Thread.Join(超时)并从总超时中删除加入所花费的时间?

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

Edit: code is now less pseudo. 编辑:现在代码更少伪。 don't understand why you would mod an answer -2 when the answer you modded +4 is exactly the same, only less detailed. 不明白为什么你会修改答案-2当你修改的答案+4完全相同,只是不那么详细。

This doesn't answer the question (no timeout), but I've made a very simple extension method to wait all threads of a collection: 这不回答问题(没有超时),但我做了一个非常简单的扩展方法来等待集合的所有线程:

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

Then you simply call: 然后你只需致电:

List<Thread> threads=new List<Thread>();
//Add your threads to this collection
threads.WaitAll();

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

I read the book C# 4.0: The Complete Reference of Herbert Schildt. 我读过C#4.0:Herbert Schildt的完整参考书。 The author use join to give a solution : 作者使用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();
        }
    }

I was tying to figure out how to do this but i could not get any answers from google. 我想弄清楚如何做到这一点,但我无法从谷歌得到任何答案。 I know this is an old thread but here was my solution: 我知道这是一个旧线程,但这是我的解决方案:

Use the following class: 使用以下类:

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. Call Addthreads(int numThreads) before executing a thread(s). 在执行线程之前调用Addthreads(int numThreads)。
  2. Call RemoveThread() after each one has completed. 每个完成后调用RemoveThread()。
  3. Use Wait() at the point that you want to wait for all the threads to complete before continuing 在继续之前,在等待所有线程完成的位置使用Wait()

Possible solution: 可能的方法:

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

Assuming dataList is the list of items and each item needs to be processed in a separate thread. 假设dataList是项目列表,每个项目需要在单独的线程中处理。

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

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