当完成时,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");
    }
}

解决方法

Task Parallel Library(TPL)于2010年推出之后,BackgroundWorker类实际上已过时。如果您要做的工作结果是同质的,则可以使用{{ 3}}方法,或者,如果您熟悉LINQ,请使用Parallel.ForEach。这是一个PLINQ示例:

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

如果结果不同,则可以为每个工作创建一个PLINQ,将其存储在List<Task>中,等待所有任务,然后通过Task<TResult>获取结果每个属性。示例:

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;

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...