我通常推荐在使用.NET 4.5时使用
Task
和/或
await
。但是
Task
和BGW有两个截然不同的场景。Task适用于一般的短异步任务,可以链接到一个连续体,而await适用于隐式地将任务编组回UI线程。BGW适用于单个长时间操作,不应影响您的UI的响应能力。您可以将BGW拖放到设计表面并双击以创建事件处理程序。如果您不想编组到另一个线程,则无需处理
LongRunning
或
ConfigureAwait
。许多人发现BGW进度比更容易。
以下是在“漫长操作”场景中同时使用两者的一些示例:
由于问题特别提到了.NET 4.0,因此以下是使用
Task
执行漫长操作并向UI提供进度的简单代码:
startButton.Enabled = false;
var task = Task.Factory.
StartNew(() =>
{
foreach (var x in Enumerable.Range(1, 10))
{
var progress = x*10;
Thread.Sleep(500);
BeginInvoke((Action) delegate {
progressBar1.Value = progress;
});
}
}, TaskCreationOptions.LongRunning)
.ContinueWith(t =>
{
startButton.Enabled = true;
progressBar1.Value = 0;
});
使用 BackgroundWorker
的类似代码可能是:
startButton.Enabled = false;
BackgroundWorker bgw = new BackgroundWorker { WorkerReportsProgress = true };
bgw.ProgressChanged += (sender, args) =>
{ progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted += (sender, args) =>
{
startButton.Enabled = true;
progressBar1.Value = 0;
};
bgw.DoWork += (sender, args) =>
{
foreach (var x in Enumerable.Range(1, 10))
{
Thread.Sleep(500);
((BackgroundWorker)sender).ReportProgress(x * 10);
}
};
bgw.RunWorkerAsync();
现在,如果您使用的是.NET 4.5,则可以使用Progress<T>
代替使用Task
中的BeginInvoke
调用。而且,由于在4.5中使用await
可能更易读:
startButton.Enabled = false;
var pr = new Progress<int>();
pr.ProgressChanged += (o, i) => progressBar1.Value = i;
await Task.Factory.
StartNew(() =>
{
foreach (var x in Enumerable.Range(1, 10))
{
Thread.Sleep(500);
((IProgress<int>) pr).Report(x*10);
}
}, TaskCreationOptions.LongRunning);
startButton.Enabled = true;
progressBar1.Value = 0;
使用
Progress<T>
会使代码与特定的UI框架(即对
BeginInvoke
的调用)解耦,就像
BackgroundWorker
有助于从特定的UI框架中解耦一样。如果您不关心这个问题,那么您就不需要引入使用
Progress<T>
的复杂性。
至于
LongRunning
,正如Stephen Toub所说:“您通常只会在通过性能测试发现不使用它会导致其他工作处理时间过长时才使用LongRunning”,因此,如果您发现需要使用它,则使用它-没有额外的分析或只是“复杂性”始终添加
LongRunning
参数。不使用 LongRunning 意味着用于长时间运行操作的线程池线程将无法用于其他更短暂的任务,并且可能会强制线程池在启动另一个线程(至少第二个)时延迟启动其中一个短暂任务。
框架中没有特别指出 BGW(或EAP、APM)已经
过时的特性。因此,您需要决定何时将这些内容视为“过时”。BGW具有非常特定的使用场景,这仍然适用于它。在 .NET 4.0和4.5 中有相当不错的替代方法,但我并不认为 BGW 是“过时”的。
我不是说一定要使用
BackgroundWorker
,我只是在说,在废弃
BackgroundWorker
之前,请先
考虑。在某些情况下,它可能是更好的选择。
Progress<>
变量上进行闭包操作:processor.ItemProcessed += (sender, e1) => ((IProgress<int>) p).Report(e1.NextItem);
,然后它就可以正常工作了 ™。 - Chris MarisicBackgroundWorker
仅适用于WinForm表单。 - Peter Ritchie