简体   繁体   English

如何让等待所有线程完成它在c#中的线程池中工作

[英]how to make Wait until all threads finished it's work from threadpool in c#

for (int task = 0; task < 20; task++)
{

    ThreadPool.QueueUserWorkItem(new WaitCallback(TaskCallBack), new object[] { filepath1, filepath2, 
                  filepath3 });

}

public static void TaskCallBack(object state)
{
            object[] array = state as object[];
            string filea = Convert.ToString(array[0]);
            string fileb = Convert.ToString(array[1]);
            string filec = Convert.ToString(array[2]);
            //something below         

}

I want main thread to be waited until all threads finishes its work.我希望主线程等待所有线程完成其工作。 Please help请帮忙

The best way to handle this would be to use Task.Run() and Task.WhenAll() , or to use Parallel.Invoke() .处理此问题的最佳方法是使用Task.Run()Task.WhenAll() ,或使用Parallel.Invoke()

However, if you need to use ThreadPool.QueueUserWorkItem you can solve this issue as follows:但是,如果您需要使用ThreadPool.QueueUserWorkItem您可以按如下方式解决此问题:

  1. For ease of use, encapsulate all the data that you want to pass to the thread in a class.为了便于使用,将所有要传递给线程的数据封装在一个类中。 This data should include an instance of CountdownEvent initialised with a count equal to the number of threads you want to wait for.此数据应包括一个CountdownEvent实例,该实例初始化的计数等于您要等待的线程数。 (In the sample code below, this class is called ThreadData .) (在下面的示例代码中,此类称为ThreadData 。)
  2. Inside your TaskCallBack() methods, call CountdownEvent.Signal() when the method has completed.TaskCallBack()方法中,当方法完成时调用CountdownEvent.Signal()
  3. Inside the main thread, start all the threadpool threads and then call CountdownEvent.Wait() to wait for all the threads to complete.在主线程内部,启动所有线程池线程,然后调用CountdownEvent.Wait()等待所有线程完成。

Putting this all together in a compilable console app:将所有这些放在一个可编译的控制台应用程序中:

using System;
using System.Threading;

namespace CoreConsole
{
    public sealed class ThreadData
    {
        public ThreadData(CountdownEvent countdown, int index)
        {
            Countdown = countdown;
            Index     = index;
        }

        public CountdownEvent Countdown { get; }
        public int Index { get; }
    }

    public static class Program
    {
        static void Main()
        {
            int n = 20;
            var countdown  = new CountdownEvent(n);

            for (int task = 0; task < n; task++)
            {
                ThreadPool.QueueUserWorkItem(TaskCallBack, new ThreadData(countdown, task));
            }

            Console.WriteLine("Waiting for all threads to exit");

            countdown.Wait();

            Console.WriteLine("Waited for all threads to exit");
        }

        public static void TaskCallBack(object state)
        {
            var data = (ThreadData) state;

            Console.WriteLine($"Thread {data.Index} is starting.");

            Thread.Sleep(_rng.Next(2000, 10000));
            data.Countdown.Signal();

            Console.WriteLine($"Thread {data.Index} has finished.");
        }

        static readonly Random _rng = new Random(45698);
    }
}

The ThreadData.Index property is just used to identify each thread in the Console.WriteLine() calls. ThreadData.Index属性仅用于标识Console.WriteLine()调用中的每个线程。

Note: In real code, it is important to always signal the Countdown event, even if the thread throws an exception - so you should wrap the code in a try/finally like so:注意:在实际代码中,即使线程抛出异常,始终发出Countdown事件信号很重要 - 因此您应该将代码包装在 try/finally 中,如下所示:

public static void TaskCallBack(object state)
{
    var data = (ThreadData)state;

    try
    {
        // Do work here.
        Console.WriteLine($"Thread {data.Index} is starting.");
        Thread.Sleep(_rng.Next(2000, 10000));
        Console.WriteLine($"Thread {data.Index} has finished.");
    }

    finally
    {
        data.Countdown.Signal();
    }
}

Like @Ackdari mentioned in his comment, you could use Task.Run .就像@Ackdari 在他的评论中提到的那样,您可以使用Task.Run But you don't need the await keyword.但是您不需要await关键字。 Just create a collection with the tasks and wait for it.只需使用任务创建一个集合并等待它。

Example:例子:

// Create a list that will hold the tasks
List<Task> tasks = new List<Task>;

// Create the tasks
for (int taskId = 0; taskId < 20; task++)
{
  tasks.Add(Task.Run(() => { TaskCallBack(new object[] { filepath1, filepath2, filepath3 }); }));
}

// Wait for ALL tasks to complete
Task.WaitAll(tasks.ToArray());

That way you could also use your own method that will be run by the task.这样你也可以使用你自己的方法来运行任务。 Example:例子:

public static void ReplaceWithABetterName(string[] filePaths)
{
   string filea = filePaths[0);
   string fileb = filePaths[1];
   string filec = filePaths[2];
   //do stuff       
}

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

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