C# 中的后台工作队列






4.74/5 (12投票s)
如何实现后台工作队列
引言
我需要创建一个后台工作线程队列,以便将操作添加到队列中,并在第一个操作完成后运行下一个操作,直到队列为空。
背景
我发现这在TreeView项目选择时很有用,在选择时我们需要执行某些操作,但要按顺序执行,而不会冻结 UI。
使用代码
CustomWorker 类是 BackgroundWorker 的一个包装类,它具有 sender 属性。sender 属性可以设置为任何执行操作的 sender,在我的例子中,sender 是 TreeView 中选定的复选框项目。
"QueueWorker" 函数接受队列对象、sender 以及每个工作线程需要执行的所有必需操作。在这个例子中,我使用了 DoWork、WorkerCompleted、ProgressChanged 和一个自定义操作,该操作用于在工作线程队列为空时显示任何错误。
public class CustomWorker : BackgroundWorker
{
public CustomWorker(object sender)
{
this.Sender = sender;
}
public object Sender { get; private set; }
public static void QueueWorker(
Queue<CustomWorker> queue,
object item,
Action<object, DoWorkEventArgs> action,
Action<object, RunWorkerCompletedEventArgs> actionComplete,
Action<RunWorkerCompletedEventArgs> displayError,
Action<object, ProgressChangedEventArgs> progressChange)
{
if (queue == null)
throw new ArgumentNullException("queue");
using (var worker = new CustomWorker(item))
{
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.ProgressChanged += (sender, args) =>
{
progressChange.Invoke(sender, args);
};
worker.DoWork += (sender, args) =>
{
action.Invoke(sender, args);
};
worker.RunWorkerCompleted += (sender, args) =>
{
actionComplete.Invoke(sender, args);
queue.Dequeue();
if (queue.Count > 0)
{
var next = queue.Peek();
next.ReportProgress(0, "Performing operation...");
next.RunWorkerAsync(next.Sender);
}
else
displayError.Invoke(args);
};
queue.Enqueue(worker);
if (queue.Count == 1)
{
var next = queue.Peek();
next.ReportProgress(0, "Performing operation...");
next.RunWorkerAsync(next.Sender);
}
}
}
}
CustomWorker 的用法如下,传递一个空的队列对象、sender 和操作。
QueueWorker 函数将通过为每个操作创建后台工作线程并将它们排队执行来更新队列。
var workerQueue = new Queue<CustomWorker>();
private void SelectTreeViewCheckBox(object sender)
{
CustomWorker.QueueWorker(
this.workerQueue,
sender,
(x, e) =>
{
//// some custom do work logic.
},
(x, e) =>
{
//// some custom completed logic.
},
(e) =>
{
//// some custom display error logic.
},
(x, e) =>
{
//// Progress change logic.
this.ProgressValue = e.ProgressPercentage;
this.Status = e.UserState.ToString();
});
}
关注点
我使用 BackgroundWorker 在TreeView 的每个复选框选择上执行一些操作,并且所有选择之间存在依赖关系,因此我需要使用锁定来避免不同的工作线程在同一时间更新对象。一旦我实现了这个工作线程队列,我就不再需要锁定,并且选择的处理变得无缝。
历史
1.0