C#:等待所有线程完成

时间:2021-07-21 01:10:07

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.

但是,对于这种事情,似乎应该有一个更简单的习语。

9 个解决方案

#1


25  

I still think using Join is simpler. Record the expected completion time (as Now+timeout), then, in a loop, do

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

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

#2


22  

With .NET 4.0 I find System.Threading.Tasks a lot easier to work with. 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.

使用.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  

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

#4


8  

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完全相同,只是不那么详细。

#5


7  

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

#6


6  

This may not be an option for you, but if you can use the Parallel Extension for .NET then you could use Tasks instead of raw threads and then use Task.WaitAll() to wait for them to complete.

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

#7


1  

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).
  2. 在执行线程之前调用Addthreads(int numThreads)。

  3. Call RemoveThread() after each one has completed.
  4. 每个完成后调用RemoveThread()。

  5. Use Wait() at the point that you want to wait for all the threads to complete before continuing
  6. 在继续之前,在等待所有线程完成的位置使用Wait()

#8


1  

I read the book C# 4.0: The Complete Reference of Herbert Schildt. The author use join to give a solution :

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

#9


0  

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是项目列表,并且每个项目都需要在单独的线程中处理。

#1


25  

I still think using Join is simpler. Record the expected completion time (as Now+timeout), then, in a loop, do

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

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

#2


22  

With .NET 4.0 I find System.Threading.Tasks a lot easier to work with. 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.

使用.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  

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

#4


8  

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完全相同,只是不那么详细。

#5


7  

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

#6


6  

This may not be an option for you, but if you can use the Parallel Extension for .NET then you could use Tasks instead of raw threads and then use Task.WaitAll() to wait for them to complete.

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

#7


1  

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).
  2. 在执行线程之前调用Addthreads(int numThreads)。

  3. Call RemoveThread() after each one has completed.
  4. 每个完成后调用RemoveThread()。

  5. Use Wait() at the point that you want to wait for all the threads to complete before continuing
  6. 在继续之前,在等待所有线程完成的位置使用Wait()

#8


1  

I read the book C# 4.0: The Complete Reference of Herbert Schildt. The author use join to give a solution :

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

#9


0  

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是项目列表,并且每个项目都需要在单独的线程中处理。