我需要创建队列并将其与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个参数
我该如何解决这个问题?我该如何更改参数?感谢
答案 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);
}
);
}