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

时间:2013-05-03 16:09:00

标签: c# wpf task-parallel-library backgroundworker async-await

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

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

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

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

我看到(矛盾)关于

的并发观点

异步模式:

  

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

我仍有疑问:

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

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

3 个答案:

答案 0 :(得分:15)

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

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

由于该问题特别提及.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<T>而不是使用BeginInvoke进行Task调用。从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); // 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意味着用于长时间运行操作的线程池线程将无法用于其他更短暂的任务,并且可能会强制线程池在启动另一个线程时延迟启动其中一个临时任务(至少是秒)。

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

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

答案 1 :(得分:14)

我认为这些模式(特别是APM,EAP和BGW)在.NET 4.5中已经过时了。 asyncTask.Run的组合在各个方面都优于BGW。事实上,我刚刚开始series on my blog我会将BGW与Task.Run进行比较,并展示在各种情况下它是如何更麻烦的;在某些情况下,它只是略微更麻烦,但还有其他情况,它很多更麻烦。

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

更新:我最近更新了我的一篇旧博客帖子discuss various implementations of background operations。在那篇文章中,当我谈到“任务(异步方法)”时,我的意思是将Task与所有.NET 4.5 async支持,Task.Run等一起使用。“任务(任务)并行库)“部分正在评估.NET 4.0中存在的Task

答案 2 :(得分:1)

我的问题是:

  

是不是.NET 4.0 TPL制作了APM,EAP和BackgroundWorker   异步模式已经过时了?

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

我很高兴在他的代码项目文章"Task Parallel Library and async-await Functionality - Patterns of Usage in Easy Samples"中找到Nick Polyak的答案:

  • “.NET 4.5的大部分功能已更改为”任务“BackgroundWorker 现在已经过时,但在需要使用EAP时仍可能遇到某些情况在这种情况下,从EAP功能中生成Task对象会很好,这样您就可以安排它们并行运行或者等待之前的许多任务完成等等。在本节中我们将展示如何实现它“
  • “让任务功能BackgroundWorker功能更强大已过时 - 您可以使用Task而不是BackgroundWorker实现您需要的任何东西。仍然可能有某些原因要使用{{ 1}}团队中的功能 - 无论是因为您的遗留代码使用它还是因为您的大多数团队成员或您的老板都喜欢它并且比新的任务功能更好地理解它“

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