Async/await vs BackgroundWorker Async/await和BackgroundWorker都是用于在应用程序中执行异步操作的机制。但是它们在使用上有一些区别。 Async/await是C# 5.0引入的一种语言级别的异步编程模型。它允许开发人员编写更简洁、更易读的异步代码。使用async关键字定义一个异步方法,并使用await关键字等待异步操作完成。这样可以避免使用回调函数或事件处理程序来处理异步操作的结果。 BackgroundWorker是.NET框架提供的一个用于
Async/await vs BackgroundWorker Async/await和BackgroundWorker都是用于在应用程序中执行异步操作的机制。但是它们在使用上有一些区别。 Async/await是C# 5.0引入的一种语言级别的异步编程模型。它允许开发人员编写更简洁、更易读的异步代码。使用async关键字定义一个异步方法,并使用await关键字等待异步操作完成。这样可以避免使用回调函数或事件处理程序来处理异步操作的结果。 BackgroundWorker是.NET框架提供的一个用于
最近几天,我测试了.net 4.5和c# 5的新功能。
我喜欢它的新异步/等待功能。之前我使用BackgroundWorker在后台处理较长的过程,同时保持用户界面的响应性。
我的问题是:在有了这些新功能之后,我应该什么时候使用异步/等待,什么时候使用BackgroundWorker?它们的常见使用场景是什么?
Async/await vs BackgroundWorker:为什么引入了Async/await关键词,以及解决方法
在异步编程中,我们经常会遇到需要执行长时间运行的操作,比如I/O操作或者计算密集型操作。在过去,我们通常使用BackgroundWorker来处理这些操作,但是微软在最新的.NET框架中引入了新的关键词Async/await来解决这个问题。
Async/await关键词的出现是为了解决异步操作的问题。在过去,我们通常使用BackgroundWorker来处理长时间运行的操作。然而,使用BackgroundWorker需要编写更多的代码,并且需要处理竞态条件。竞态条件是指当多个线程同时访问共享资源时可能发生的问题,比如多个线程同时写入同一个变量,可能导致数据损坏或者不一致。而Async/await关键词则可以避免这些问题。
Async/await关键词的工作原理是将异步操作转化为一系列的回调函数。当一个异步操作开始时,它会返回一个Task对象,并将后续的代码封装为一个回调函数。当异步操作完成时,它会调用回调函数,并传递操作的结果。在这个过程中,当前线程不会被阻塞,可以继续执行其他操作。
相比之下,BackgroundWorker需要手动管理线程。我们需要创建一个后台线程来执行操作,并在操作完成后通知主线程。这种方式需要编写更多的代码,并且容易出现竞态条件。
在处理I/O密集型操作时,Async/await关键词的优势更加明显。由于异步操作不会阻塞当前线程,我们可以同时执行其他操作。而使用BackgroundWorker时,我们需要手动创建和管理线程,会造成线程的浪费。
在处理计算密集型操作时,Async/await关键词结合Task.Run方法可以更好地利用线程池。Task.Run方法可以将操作移动到后台线程上执行,而不需要手动创建和管理线程。这样可以更好地利用系统资源,提高性能。
Async/await关键词是一种更好的异步编程方式。它比BackgroundWorker更简洁,避免了竞态条件的问题。在处理I/O密集型操作时,Async/await关键词可以更好地利用系统资源。在处理计算密集型操作时,Async/await关键词结合Task.Run方法可以更好地利用线程池。
总之,Async/await关键词的引入解决了异步编程中的一些常见问题,提供了一种更加简洁和高效的编程方式。在未来的开发中,我们应该尽可能地使用Async/await关键词来处理异步操作。
Async/await与BackgroundWorker的比较是一个常见的讨论话题。这两者之间的比较就像是比较苹果和橙子,原因如下:
1. BackgroundWorker是用来在后台线程池中执行单个任务的。而async/await是一种语法,用于异步等待异步操作的完成。这些操作可能使用线程池线程,也可能不使用任何其他线程。
2. 使用async/await可以实现以下操作:
using (WebResponse response = await webReq.GetResponseAsync()) { using (Stream responseStream = response.GetResponseStream()) { int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length); } }
但是,你不太可能在BackgroundWorker中以这种方式建模,你可能会在.NET 4.0中使用以下方式:
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语句。
3. BackgroundWorker通常用于建模不希望影响UI响应性的单个长时间运行的操作。例如:
BackgroundWorker worker = new BackgroundWorker(); worker.DoWork += (sender, e) => { int i = 0; // 模拟耗时操作 Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }; worker.RunWorkerCompleted += (sender, eventArgs) => { // 在UI线程上执行某些操作,如更新状态或显示“结果” }; worker.RunWorkerAsync();
在这里,你无法使用async/await进行操作,BackgroundWorker会为你创建线程。
4. 你可以使用TPL来替代BackgroundWorker:
var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => { int i = 0; // 模拟耗时操作 Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }).ContinueWith(t => { // 在UI线程上执行某些操作,如更新状态或显示“结果” }, synchronizationContext);
在这种情况下,TaskScheduler会为你创建线程(假设使用默认的TaskScheduler),并且可以使用await进行操作:
await Task.Factory.StartNew(() => { int i = 0; // 模拟耗时操作 Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) ++i; }); // 在UI线程上执行某些操作,如更新状态或显示“结果”
5. 在我看来,一个重要的比较是是否报告进度。例如,你可能有一个像这样的BackgroundWorker:
BackgroundWorker worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.ProgressChanged += (sender, eventArgs) => { // 处理进度,如更新进度条 }; worker.DoWork += (sender, e) => { int i = 0; // 模拟耗时操作 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) => { // 在UI线程上执行某些操作,如更新状态或显示“结果” }; worker.RunWorkerAsync();
但是,在使用async/await时,你可以这样做:
IProgressprogress = new Progress (); progress.ProgressChanged += (s, e) => { // 处理e.ProgressPercentage,如更新进度条 }; await Task.Factory.StartNew(() => { int i = 0; // 模拟耗时操作 Stopwatch sw = Stopwatch.StartNew(); while (sw.Elapsed.TotalSeconds < 1) { if ((sw.Elapsed.TotalMilliseconds%100) == 0) { progress.Report((int) (1000 / sw.ElapsedMilliseconds)) } ++i; } }); // 在UI线程上执行某些操作,如更新状态或显示“结果”
是否报告进度是一个重要的比较因素。使用BackgroundWorker可以很方便地报告进度,但是在使用async/await时,你需要使用IProgress
另外,需要注意的是,使用async/await可以同时启动多个异步任务,而BackgroundWorker不能很好地处理这个问题。
Async/await vs BackgroundWorker这个问题的出现的原因是,async/await是为了替代BackgroundWorker而设计的。虽然你可以选择使用哪一种,但使用async/await以及其他几个TPL工具,你应该能够处理所有的情况。
解决方法是,根据个人喜好选择使用哪一种。哪一种对你来说更快?哪一种更容易理解?
Microsoft花了很多时间和精力来实现async/await,如果它没有任何优势,他们就不会费这个劲。
新的await功能使得旧的BackgroundWorker显得完全不如它。这种差异是如此显著。
在我的博客上,我有一个很好的介绍,比较了不同的后台任务处理方法。注意,async/await也允许无需线程池线程进行异步编程。
这个答案被投票下降了,这是误导人的。Async/await并不是为了替代BackgroundWorker而设计的。
当然是的。它不仅仅是为了做那件事而设计的,但是在开发过程中,async/await的一个主要用例是协助处理BGW设计处理的确切情况,即在UI应用程序中异步执行某些工作,并在工作完成时更新UI。
它不是一对一的替代,因为async/await并不在单独的线程上执行操作。BackgroundWorker允许任务真正并行运行,而async/await只是委托了工作的顺序。现在,你是否真的需要一个单独的线程取决于你自己。
当你加入其他的TPL功能时,它实际上提供了所有这些功能。具体来说,Task.Run就足以创建一个表示在另一个线程上执行的CPU绑定工作的任务。我在问题中明确说过,async/await与TPL结合在一起就能处理BGW的所有功能。
它是为了替代BackgroundWorker中那些被用作解决方案以使应用程序响应的任务而设计的。它不是真正后台工作的替代品,那些需要在后台运行的工作,可能会不时地通知用户(自动保存、下载等)。
你为什么认为TPL不能用于这些类型的用例呢?它缺少哪些功能,以解决这些问题?
可能听起来很愚蠢,但我不得不使用BGW,因为我无法在异步操作正在进行时移动进度条。
这就是Progress类的存在目的。