使用队列创建BackgroundWorker

时间:2013-02-27 18:53:02

标签: c# wpf generics queue backgroundworker

我需要创建队列并将其与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个参数

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

2 个答案:

答案 0 :(得分:29)

这是一个更短的方法,可以做你想要的:

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 :(得分: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);
                }
            );
    }