不是.NET 4.0 TPL使APM,EAP和BackgroundWorker异步模式过时了吗?

我有2种C#WPF应用程序项目:

  • 基于.NET 4.0,我无法迁移到.NET 4.5
  • 基于.NET 4.0,我可以迁移到.NET 4.5

所有这些都应该产生2-10个长时间运行(天)进程,这些进程可以被用户取消和重新启动。

我有兴趣遵循最佳设计实践。 首先,现在,我有兴趣消除有关BackgroundWorker用法的歧义,但我希望,我的问题应该对其他异步模式有效。

我看到(矛盾)并发的观点

  • 异步编程模型(APM)
  • 基于事件的异步模式(EAP)
  • 的BackgroundWorker

异步模式:

  • A).NET 4.5使它们过时了

    • 在约瑟夫·阿尔巴哈里的书中,Ben Albahari在“ 过时模式 ”子章节中的“C#5.0 in a Nutshell:The Definitive Reference”中提到了这一点,而其上一版“C#4.0 in a Nutshell:The Definitive Reference”中没有
    • MSDN文章“使用异步和等待的异步编程(C#和Visual Basic)”告诉:

“基于异步的异步编程方法几乎在所有情况下都优于现有方法。特别是,对于IO绑定操作,这种方法优于BackgroundWorker ,因为代码更简单,您无需防范竞争条件。与Task.Run结合使用,异步编程优于BackgroundWorker用于CPU绑定操作,因为异步编程将运行代码的协调细节与Task.Run传输到线程池的工作分开“

  • B)他们(或者至少是BackgroundWorker )在.NET 4.5中并没有过时

我仍有疑问:

  1. 这些模式(首先是BGW)在.NET 4.5中是否已过时?
  2. 如果它们在.NET 4.5中已经过时,为什么它们在.NET 4.0中不会过时?

    2A)我是否错误地理解.NET 4.0新function在.NET 4.0中仍然“易于”实现/重现?

我通常建议使用Task和/或await使用.NET 4.5。 但Task和BGW有两种截然不同的情景。 任务适用于可以链接到延续的一般短异步任务,并且等待擅长隐式编组回UI线程的任务。 BGW适用于单个长操作,不会影响UI的响应性。 您可以将BGW拖放到设计图面上,然后双击以创建事件处理程序。 如果您不想编组到另一个线程,则不必处理LongRunningConfigureAwait 。 许多人发现BGW的进展比IProgress更容易。

以下是在“冗长操作”场景中使用两者的一些示例:

由于该问题特别提到了.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); // 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,则可以使用Progress代替使用TaskBeginInvoke调用。 从4.5开始,使用await可能更具可读性:

 startButton.Enabled = false; var pr = new Progress(); 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) pr).Report(x*10); } }, TaskCreationOptions.LongRunning); startButton.Enabled = true; progressBar1.Value = 0; 

使用Progress意味着代码没有耦合到特定的UI框架(即对BeginInvoke的调用),这与BackgroundWorker促进从特定UI框架分离的方式非常相似。 如果你不在乎,那么你不需要介绍使用Progress增加的复杂性

至于LongRunning ,正如Stephen Toub所说:“你通常只使用LongRunning,如果你通过性能测试发现没有使用它会导致其他工作处理的长时间延迟”所以,如果你发现你需要使用它,那么你使用它 – 增加的分析或只是总是添加LongRunning参数的“复杂性”。 不使用LongRunning意味着用于长时间运行操作的线程池线程将无法用于其他更短暂的任务,并且可能会强制线程池在启动另一个线程时延迟启动其中一个临时任务(至少一个第二)。

框架中没有任何属性明确表示BGW(或EAP或APM)已被弃用 。 因此,由您来决定这些东西何时何地“过时”。 BGW特别总是有一个非常具体的使用场景仍然适用于它。 你在.NET 4.0和4.5中有相当不错的选择; 但我真的不认为BGW是“过时的”。

我并不是说总是使用BackgroundWorker ,我只是说在你自动弃用BackgroundWorker之前想一想 ,在某些情况下它可能是一个更好的选择。

我认为这些模式(特别是APM,EAP和BGW)在.NET 4.5中已经过时了。 asyncTask.Run的组合在各个方面都优于BGW。 事实上,我刚刚在我的博客上开始了一个系列 ,我将把BGW与Task.Run进行比较,并展示它在各种情况下的繁琐程度; 在某些情况下,它只是稍微麻烦一点,但在其他情况下,它更加繁琐。

现在,它们是否在.NET 4.0中已经过时完全是另一个问题。 在您的其他post中,您谈到的是使用VS2010开发.NET 4.0,因此无法选择反向端口Microsoft.Bcl.Async 。 在这种情况下,APM和EAP都不能被视为过时的IMO。 在这个平台上,您可以将Task.Factory.StartNew视为BGW的替代品,但BGW在进度报告和自动线程封送其进度和完成事件方面确实具有一些优势。

更新:我最近更新了我的一篇旧博客文章,在那里我讨论了后台操作的各种实现 。 在Task.Run文章中,当我谈到“任务(异步方法)”时,我的意思是使用Task与所有.NET 4.5 async支持, Task.Run等。“任务(任务并行库)”部分正在评估Task因为它存在于.NET 4.0中。

我的问题是:

不是.NET 4.0 TPL使APM,EAP和BackgroundWorker异步模式过时了吗?

即怀疑,要求确认或否定如果.NET 4.5中的某些东西已经过时,那么我不明白为什么以及它如何在.NET 4.0中有所不同,即使用任务并行库(没有async/await参与和.NET) 4.5专有支持)

我很高兴在他的代码项目文章“任务并行库和异步 – 等待function – 简单样本中的使用模式”中找到Nick Polyak的答案:

  • “.NET 4.5的大部分function已经改为任务,而BackgroundWorker 现在已经过时了 ,但是当你需要使用EAP模式时,你仍然会遇到一些情况。在这种情况下,生成Task对象会很好超出EAPfunction,以便您能够安排它们并行运行或等待许多先前的任务完成等。在本节中,我们将展示如何实现它“
  • “使任务function使BackgroundWorkerfunction变得过时 – 您可以使用Task而不是BackgroundWorker来实现您所需的任何function。仍然可能有一些原因您希望在团队中使用BackgroundWorkerfunction – 无论是因为您的遗留代码使用它还是因为大多数您的团队成员或您的老板喜欢它并且比新的任务function更好地理解它“

仍然愿意看到任何不同的争论观点