使用队列创建BackgroundWorker

10

我需要创建队列并与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; }
}

但是我在 doWork 和 workerCompleted 中遇到了问题。我收到了以下错误信息:

委托“Func”不接受1个参数

我该如何解决这个问题?我应该如何更改参数?谢谢。

1
至少指出在哪一行。也许只发布那一行。 - H H
2个回答

35
这里有一个更短的方法可以实现你想要的功能:
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;
        }
    }
}

通过将每个新动作作为上一个动作的延续添加,您确保一次只处理一个动作,因为下一个项目不会开始,直到前一个项目完成,您确保当没有要处理的内容时,没有线程处于空闲状态,并且您确保它们按顺序完成。
此外,请注意,如果您认为只需要一个队列而不是任何数量,您可以将所有成员设置为static,但这取决于您。

1
看起来更好了,谢谢你。现在我是这样使用它的:bgQueue.QueueTask(() => CreateConvertingProccess(item.FullName));。你能帮我吗,当一个任务完成后如何调用方法?我应该在锁之后添加它吗?我的意思是当我想要添加一个方法时,它将被添加到RunWorkerCompleted中。 - Libor Zapletal
非常感谢,它正在工作。我必须学习如何使用任务。 - Libor Zapletal
你能再帮我一次吗?请问如何判断队列是否为空? - Libor Zapletal
2
public bool IsEmpty() { return previousTask.IsCompleted; } - Servy
@servy 这很有趣...你能分享一个例子吗?比如如何处理文件列表(给定文件路径)? - Geovani Martinez
显示剩余3条评论

1

看起来您缺少第二个泛型参数 - Tout;

以下代码应该可以解决这个问题:

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; }
}

"而且使用方法应该是:"
    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);
                }
            );
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接