wpf 使用队列创建 BackgroundWorker

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15119974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 07:45:50  来源:igfitidea点击:

Creating BackgroundWorker with Queue

c#wpfgenericsqueuebackgroundworker

提问by Libor Zapletal

I need to create queue and use it with BackgroundWorker. So I can add operations and when one is done next is starting in background. I found this code by google:

我需要创建队列并将其与 BackgroundWorker 一起使用。所以我可以添加操作,下一个完成时在后台开始。我通过谷歌找到了这个代码:

public class QueuedBackgroundWorker<T>
{
    public void QueueWorkItem(
        Queue queue,
        T inputArgument,
        Func<T> doWork,
        Action workerCompleted)
    {
        if (queue == null) throw new ArgumentNullException("queue");

        BackgroundWorker bw = new BackgroundWorker();
        bw.WorkerReportsProgress = false;
        bw.WorkerSupportsCancellation = false;
        bw.DoWork += (sender, args) =>
        {
            if (doWork != null)
            {
                args.Result = doWork(new DoWorkArgument<T>((T)args.Argument));
            }
        };
        bw.RunWorkerCompleted += (sender, args) =>
        {
            if (workerCompleted != null)
            {
                workerCompleted(new WorkerResult<T>((T)args.Result, args.Error));
            }
            queue.Dequeue();
            if (queue.Count > 0)
            {
                QueueItem<T> nextItem = queue.Peek() as QueueItem<T>;
                nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
            }
        };

        queue.Enqueue(new QueueItem<T>(bw, inputArgument));
        if (queue.Count == 1)
        {
            QueueItem<T> nextItem = queue.Peek() as QueueItem<T>;
            nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
        }
    }
}

public class DoWorkArgument<T>
{
    public DoWorkArgument(T argument)
    {
        this.Argument = argument;
    }
    public T Argument { get; private set; }
}

public class WorkerResult<T>
{
    public WorkerResult(T result, Exception error)
    {
        this.Result = result;
        this.Error = error;
    }

    public T Result { get; private set; }
    public Exception Error { get; private set; }
}

public class QueueItem<T>
{
    public QueueItem(BackgroundWorker backgroundWorker, T argument)
    {
        this.BackgroundWorker = backgroundWorker;
        this.Argument = argument;
    }

    public T Argument { get; private set; }
    public BackgroundWorker BackgroundWorker { get; private set; }
}

But I have problem with doWork and workerCompleted. I get error:

但是我对 doWork 和 workerCompleted 有问题。我得到错误:

Delegate 'Func' does not take 1 arguments

委托 'Func' 不接受 1 个参数

How can I fix this? How should I change parameters? Thanks

我怎样才能解决这个问题?我应该如何更改参数?谢谢

回答by Servy

Here's a much shorter method that does what you want:

这是一个更短的方法,可以满足您的需求:

public class BackgroundQueue
{
    private Task previousTask = Task.FromResult(true);
    private object key = new object();
    public Task QueueTask(Action action)
    {
        lock (key)
        {
            previousTask = previousTask.ContinueWith(t => action()
                , CancellationToken.None
                , TaskContinuationOptions.None
                , TaskScheduler.Default);
            return previousTask;
        }
    }

    public Task<T> QueueTask<T>(Func<T> work)
    {
        lock (key)
        {
            var task = previousTask.ContinueWith(t => work()
                , CancellationToken.None
                , TaskContinuationOptions.None
                , TaskScheduler.Default);
            previousTask = task;
            return task;
        }
    }
}

By adding each new action as a continuation of the previous you ensure that only one is worked on at a time, as the next item won't start until the previous item is finished, you ensure that there is no thread sitting around idling when there is nothing to be worked on, and you ensure they're all done in order.

通过将每个新操作添加为前一个操作的延续,您可以确保一次只处理一个操作,因为在前一个项目完成之前下一个项目不会开始,您可以确保没有线程闲置在那里时没有什么可做的,你确保它们都按顺序完成。

Also note that if you only ever think you'll need one queue, and not any number, you could make all of the members static, but that's up to you.

另请注意,如果您只认为需要一个队列而不是任何数量,则可以创建所有成员static,但这取决于您。

回答by Aaron

It seems you are missing the second generic parameter - Tout;

您似乎缺少第二个通用参数 - Tout;

The following code should take care of it:

以下代码应该处理它:

using System;
using System.Collections.Generic;
using System.ComponentModel;

public static class QueuedBackgroundWorker
{
    public static void QueueWorkItem<Tin, Tout>(
        Queue<QueueItem<Tin>> queue,
        Tin inputArgument,
        Func<DoWorkArgument<Tin>, Tout> doWork,
        Action<WorkerResult<Tout>> workerCompleted)
    {
        if (queue == null) throw new ArgumentNullException("queue");

        BackgroundWorker bw = new BackgroundWorker();
        bw.WorkerReportsProgress = false;
        bw.WorkerSupportsCancellation = false;
        bw.DoWork += (sender, args) =>
            {
                if (doWork != null)
                {
                    args.Result = doWork(new DoWorkArgument<Tin>((Tin)args.Argument));
                }
            };
        bw.RunWorkerCompleted += (sender, args) =>
            {
                if (workerCompleted != null)
                {
                    workerCompleted(new WorkerResult<Tout>((Tout)args.Result, args.Error));
                }
                queue.Dequeue();
                if (queue.Count > 0)
                {
                    QueueItem<Tin> nextItem = queue.Peek(); // as QueueItem<T>;
                    nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
                }
            };

        queue.Enqueue(new QueueItem<Tin>(bw, inputArgument));
        if (queue.Count == 1)
        {
            QueueItem<Tin> nextItem = queue.Peek() as QueueItem<Tin>;
            nextItem.BackgroundWorker.RunWorkerAsync(nextItem.Argument);
        }
    }
}

public class DoWorkArgument<T>
{
    public DoWorkArgument(T argument)
    {
        this.Argument = argument;
    }

    public T Argument { get; private set; }
}

public class WorkerResult<T>
{
    public WorkerResult(T result, Exception error)
    {
        this.Result = result;
        this.Error = error;
    }

    public T Result { get; private set; }

    public Exception Error { get; private set; }
}

public class QueueItem<T>
{
    public QueueItem(BackgroundWorker backgroundWorker, T argument)
    {
        this.BackgroundWorker = backgroundWorker;
        this.Argument = argument;
    }

    public T Argument { get; private set; }

    public BackgroundWorker BackgroundWorker { get; private set; }
}

And the usage should be:

用法应该是:

    private readonly Queue<QueueItem<int>> _workerQueue = new Queue<QueueItem<int>>();
    private int _workerId = 1;

    [Test]
    public void BackgroundTest()
    {
        QueuedBackgroundWorker.QueueWorkItem(
            this._workerQueue, 
            this._workerId++,
            args =>  // DoWork
                {
                    var currentTaskId = args.Argument;
                    var now = DateTime.Now.ToLongTimeString();
                    var message = string.Format("DoWork thread started at '{0}': Task Number={1}", now, currentTaskId);
                    return new { WorkerId = currentTaskId, Message = message };
                },
            args =>  // RunWorkerCompleted
                {
                    var currentWorkerId = args.Result.WorkerId;
                    var msg = args.Result.Message;

                    var now  = DateTime.Now.ToShortTimeString();
                    var completeMessage = string.Format(
                        "RunWorkerCompleted completed at '{0}'; for Task Number={1}, DoWork Message={2}",
                        now,
                        currentWorkerId,
                        msg);
                }
            );
    }