这对许多人来说可能是 TL;DR,但是,我认为比较 await 和 BackgroundWorker 就像比较苹果和橙子,我对此的想法如下:
BackgroundWorker 旨在模拟您希望在后台执行的单个任务,在线程池线程上。 async/await 是一种用于异步等待异步操作的语法。这些操作可能使用也可能不使用线程池线程,甚至使用任何其他线程。所以,它们是苹果和橙子。
例如,您可以使用await 执行以下操作:
using (WebResponse response = await webReq.GetResponseAsync())
{
using (Stream responseStream = response.GetResponseStream())
{
int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
}
}
但是,您可能永远不会在后台工作人员中建模,您可能会在 .NET 4.0(await 之前)中执行类似的操作:
webReq.BeginGetResponse(ar =>
{
WebResponse response = webReq.EndGetResponse(ar);
Stream responseStream = response.GetResponseStream();
responseStream.BeginRead(buffer, 0, buffer.Length, ar2 =>
{
int bytesRead = responseStream.EndRead(ar2);
responseStream.Dispose();
((IDisposable) response).Dispose();
}, null);
}, null);
请注意两种语法之间的处理不相交,以及如何在没有async/await 的情况下使用using。
但是,你不会对BackgroundWorker 做这样的事情。 BackgroundWorker 通常用于对您不想影响 UI 响应能力的单个长时间运行的操作进行建模。例如:
worker.DoWork += (sender, e) =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
};
worker.RunWorkerCompleted += (sender, eventArgs) =>
{
// TODO: do something on the UI thread, like
// update status or display "result"
};
worker.RunWorkerAsync();
那里真的没有什么可以使用 async/await 的,BackgroundWorker 正在为你创建线程。
现在,您可以改用 TPL:
var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
}).ContinueWith(t=>
{
// TODO: do something on the UI thread, like
// update status or display "result"
}, synchronizationContext);
在这种情况下TaskScheduler 正在为您创建线程(假设默认为TaskScheduler),并且可以使用await,如下所示:
await Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
++i;
});
// TODO: do something on the UI thread, like
// update status or display "result"
在我看来,一个主要的比较是您是否在报告进度。例如,您可能有一个 BackgroundWorker like 这个:
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
{
// TODO: something with progress, like update progress bar
};
worker.DoWork += (sender, e) =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
{
if ((sw.Elapsed.TotalMilliseconds%100) == 0)
((BackgroundWorker)sender).ReportProgress((int) (1000 / sw.ElapsedMilliseconds));
++i;
}
};
worker.RunWorkerCompleted += (sender, eventArgs) =>
{
// do something on the UI thread, like
// update status or display "result"
};
worker.RunWorkerAsync();
但是,您不会处理其中的一些问题,因为您会将后台工作程序组件拖放到表单的设计图面上——这是您无法使用 async/@987654347 做的事情@ 和Task... 即您不会手动创建对象、设置属性和设置事件处理程序。您只需填写 DoWork、RunWorkerCompleted 和 ProgressChanged 事件处理程序的正文。
如果您将其“转换”为 async/await,您会执行以下操作:
IProgress<int> progress = new Progress<int>();
progress.ProgressChanged += ( s, e ) =>
{
// TODO: do something with e.ProgressPercentage
// like update progress bar
};
await Task.Factory.StartNew(() =>
{
int i = 0;
// simulate lengthy operation
Stopwatch sw = Stopwatch.StartNew();
while (sw.Elapsed.TotalSeconds < 1)
{
if ((sw.Elapsed.TotalMilliseconds%100) == 0)
{
progress.Report((int) (1000 / sw.ElapsedMilliseconds))
}
++i;
}
});
// TODO: do something on the UI thread, like
// update status or display "result"
如果无法将组件拖到 Designer 界面上,则真正由读者决定哪个“更好”。但是,对我来说,这是await 和BackgroundWorker 之间的比较,而不是您是否可以等待像Stream.ReadAsync 这样的内置方法。例如如果您按预期使用BackgroundWorker,则可能很难转换为使用await。
其他想法:http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html