完成后,BackgroundWorkers队列引发事件 [英] Queue of BackgroundWorkers raise event when complete

查看:50
本文介绍了完成后,BackgroundWorkers队列引发事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要执行 n BackgroundWorkers,当他们完成时,我想引发一个事件,并根据他们的所有工作来做些事情.我的用例是创建队列,填充队列,然后仅运行一次.为此,我制作了一个ParallelQueue类.经过最初的测试,它似乎可以正常工作,但是我担心条件 _max == _iteration 并不是最好的条件,那就是评估队列中的所有工作是否已经完成.还是我对Queue的使用不是线程安全的,我应该使用什么来完成此任务?(ConcurrentQueue?)如果这个问题太笼统,我将其删除,谢谢.

I need to execute n BackgroundWorkers, when they're complete I want to raise an event and do something with the result of all their work. My use case it to make the queue, fill it, then run it only once. To accomplish this I made a class ParallelQueue. With my initial testing it seems to work, however I'm concerned that the condition _max == _iteration is not the best was to evaluate all work in the queue has been done. Or that my use of Queue is not thread safe, what should I use to accomplish this? (ConcurrentQueue?) If this question is too general I'll remove it, Thanks.

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的示例

example using 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");
    }
}

推荐答案

在引入 Parallel.ForEach 方法,或者,如果您熟悉LINQ,请使用 PLINQ .这是一个PLINQ示例:

The BackgroundWorker class has become practically obsolete after the introduction of the Task Parallel Library (TPL) in 2010. If the results of the work you have to do is homogeneous, you can just use the Parallel.ForEach method, or, if you are familiar with LINQ, use PLINQ. Here is a PLINQ example:

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

如果结果不同,则可以创建 结果 的每个属性.示例:

If the results are heterogeneous, you can create a Task<TResult> for each piece of work, store it in a List<Task>, wait all the tasks, and get the results through the Result property of each. Example:

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;

这篇关于完成后,BackgroundWorkers队列引发事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆