我通常推荐Task
和/或await
如果使用 .NET 4.5。但是Task
& BGW 有两种截然不同的场景。Task 适用于可以链接到延续的一般短异步任务,而 await 适用于隐式编组回 UI 线程的任务。BGW 适用于不应影响 UI 响应能力的单个长时间操作。您可以将 BGW 拖放到设计图面上并双击以创建事件处理程序。您不必处理LongRunning
或者ConfigureAwait
如果您不想编组到另一个线程。许多人发现 BGW 的进展比IProgress<T>
.
以下是在“冗长操作”场景中同时使用两者的一些示例:
由于问题特别提到了 .NET 4.0,以下是简单的代码,它使用 aTask
执行冗长的操作,同时为 UI 提供进度:
startButton.Enabled = false;
var task = Task.Factory.
StartNew(() =>
{
foreach (var x in Enumerable.Range(1, 10))
{
var progress = x*10;
Thread.Sleep(500); // fake work
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,则可以使用 .NETProgress<T>
代替BeginInvoke
调用Task
。从 4.5 开始, usingawait
可能更具可读性:
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); // fake work
((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
参数的“复杂性” 。不使用 LongRunning 意味着用于长时间运行操作的线程池线程将不能用于其他更临时的任务,并且可能会强制线程池在启动另一个线程时延迟启动其中一个临时任务(至少第二)。
框架中没有属性专门说明 BGW(或 EAP 或 APM)已弃用。因此,由您决定这些东西在何时何地“过时”。特别是 BGW 总是有一个非常具体的使用场景,仍然适用于它。在 .NET 4.0 和 4.5 中有相当不错的选择;但我并不认为 BGW 是“过时的”。
我不是说总是使用BackgroundWorker
,我只是说在你自动弃用 BackgroundWorker 之前要三思,在某些情况下它可能是一个更好的选择。