c# - BackgroundWorkers 队列在完成时引发事件
问题描述
我需要执行n 个BackgroundWorkers,当他们完成后,我想引发一个事件并对他们所有工作的结果做一些事情。我的用例是创建队列,填充它,然后只运行一次。为此,我创建了一个类 ParallelQueue。通过我的初始测试,它似乎可以工作,但是我担心条件_max == _iteration
不是最好的评估队列中的所有工作已经完成。或者我对 Queue 的使用不是线程安全的,我应该用什么来完成这个?(ConcurrentQueue?)如果这个问题太笼统,我会删除它,谢谢。
public class ParallelQueue
{
private Queue<BackgroundWorker> _queue;
private readonly object _key = new object();
private int _max = 0;
private int _iteration = 0;
private bool _ran = false;
public ParallelQueue()
{
_queue = new Queue<BackgroundWorker>();
}
public delegate void BackgroundQueueCompleted(object sender, RunWorkerCompletedEventArgs e);
public event BackgroundQueueCompleted QueueCompleted;
public void Add(BackgroundWorker worker)
{
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(WorkerCompleted);
_queue.Enqueue(worker);
}
public void Run()
{
lock (_key)
{
if(!_queue.Any()) throw new ArgumentOutOfRangeException("ParallelQueue cannot be empty");
if (_ran) throw new InvalidOperationException("ParallelQueue can only be run once");
_ran = true;
_max = _queue.Count();
Parallel.For(0, _queue.Count, (i, state) =>
{
BackgroundWorker worker = _queue.Dequeue();
worker.RunWorkerAsync();
});
}
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Interlocked.Increment(ref _iteration);
if (_max == _iteration)
{
QueueCompleted?.Invoke(this, e);
}
}
}
使用 ParallelQueue 的示例
public class Program
{
static void Main(string[] args)
{
var queue = new ParallelQueue();
queue.QueueCompleted += MyQueueCompletedHandler;
for (int i = 0; i < 10; i++)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler((sender, e) =>
{
Thread.Sleep(500);
});
queue.Add(bw);
}
queue.Run();
Console.ReadLine();
}
private static void MyQueueCompletedHandler(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("queue is complete");
}
}
解决方案
在 2010 年引入任务并行库(TPL) 之后,BackgroundWorker
该类实际上已经过时。如果您要做的工作的结果是同质的,您可以使用该方法,或者,如果您熟悉 LINQ,则使用普林克。这是一个 PLINQ 示例:Parallel.ForEach
var input = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] results = input
.AsParallel()
.AsOrdered() // optional
.WithDegreeOfParallelism(2) // optional
.Select(x => { Thread.Sleep(500); return x * 2; }) // simulate some work
.ToArray();
如果结果是异构的,可以Task<TResult>
为每一个work创建一个,存放在a中List<Task>
,等待所有的task,通过Result
each的属性得到结果。例子:
var task1 = Task.Run(() => { Thread.Sleep(500); return 1; });
var task2 = Task.Run(() => { Thread.Sleep(500); return "Helen"; });
var task3 = Task.Run(() => { Thread.Sleep(500); return DateTime.Now; });
var list = new List<Task>() { task1, task2, task3 };
Task.WaitAll(list.ToArray());
int result1 = task1.Result;
string result2 = task2.Result;
DateTime result3 = task3.Result;
推荐阅读
- arrays - MongoDb 过滤器数组
- loopbackjs - 用户使用 AgendaJS 定义的任务
- c++ - 尝试使用 c++ 包装器从节点使用 go 函数得到段错误
- ssl - 有没有办法在不购买任何域的情况下为本地网络 webapps 使用 https?
- javascript - Mongodb $graphLookup 聚合不一致的输出顺序和排序
- reactjs - React.js Material-UI 项目无法在 codepen 上运行,尽管它在本地运行
- z3 - Z3; 使用 if-then-else 进行简化
- python - 如何在pyplot的分组条形图中标记组?
- node.js - 如何使用 Express 编辑 MongoDB 中存储的数据?
- api - 空手道 [功能自动化和性能脚本同时运行