【问题标题】:how to make Wait until all threads finished it's work from threadpool in c#如何让等到所有线程完成它从c#中的线程池工作
【发布时间】:2019-12-16 09:35:29
【问题描述】:
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
}
我希望等待主线程,直到所有线程完成其工作。请帮忙
【问题讨论】:
-
您可以切换到使用Task.Run 以及Task.WhenAll 和await,但这可能会导致您的代码发生更多变化,因为async await 在调用链中向上传播(什么是不错,但可能不需要)。或者,我建议看一下示例here。他们使用ManualResetEvent 的数组来表示操作已完成。
标签:
c#
multithreading
threadpool
【解决方案1】:
处理此问题的最佳方法是使用Task.Run() 和Task.WhenAll(),或使用Parallel.Invoke()。
但是,如果您需要使用ThreadPool.QueueUserWorkItem,您可以通过以下方式解决此问题:
- 为了方便使用,将所有要传递给线程的数据封装在一个类中。此数据应包括一个
CountdownEvent 实例,其初始化计数等于您要等待的线程数。 (在下面的示例代码中,这个类被称为ThreadData。)
- 在您的
TaskCallBack() 方法中,当方法完成时调用 CountdownEvent.Signal()。
- 在主线程内部,启动所有线程池线程,然后调用
CountdownEvent.Wait()等待所有线程完成。
将所有这些放在一个可编译的控制台应用程序中:
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);
}
}
ThreadData.Index 属性仅用于标识Console.WriteLine() 调用中的每个线程。
注意:在实际代码中,始终发出 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();
}
}
【解决方案2】:
就像@Ackdari 在他的评论中提到的那样,您可以使用 Task.Run。但是您不需要 await 关键字。只需创建一个包含任务的集合并等待它。
例子:
// 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());
这样你也可以使用你自己的方法来运行任务。
示例:
public static void ReplaceWithABetterName(string[] filePaths)
{
string filea = filePaths[0);
string fileb = filePaths[1];
string filec = filePaths[2];
//do stuff
}