难道不是.NET 4.0 TPL是由APM,EAP和BackgroundWorker的异步模式过时了

2019-09-01 22:13发布

我有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使他们过时

    • 如此命名的书由约瑟夫阿尔巴哈利,本阿尔巴哈利“C#5.0果壳中的权威参考”中的子章“ 过时的模式 ”,而其前一版“” C#4.0果壳中的权威参考”没
    • MSDN文章“异步编程与异步和等待(C#和Visual Basic)”讲述:

“基于异步方法异步编程最好在几乎所有情况下,现有的方法。尤其是,这种方法比更好的BackgroundWorker的IO绑定操作,因为代码更简单,你不必防范竞争条件。在与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.5的新功能仍然在.NET 4.0中“轻松”实现的/重现?

Answer 1:

I generally recommend Task and/or await if using .NET 4.5. But Task & BGW have 2 distinctly different scenarios. Task is good for general short asynchronous tasks that could be chained to a continuation and await is good at tasks implicitly marshalling back to the UI thread. BGW is good for a single long operation that shouldn't affect the responsiveness of your UI. You can drag-drop a BGW onto design surface and double-click to create event handlers. You don't have to deal with LongRunning or ConfigureAwait if you don't want to marshal to another thread. Many find BGW progress easier than IProgress<T>.

Here's some examples of using both in a "lengthy operation" scenario:

Since the question specifically mentions .NET 4.0, the following is simple code that uses a Task to do a lengthy operation while providing progress to a 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;
                    });

Similar code with BackgroundWorker might be:

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();

Now, if you were using .NET 4.5 you could use Progress<T> instead of the BeginInvoke call with Task. And since in 4.5, using await would likely be more readable:

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;

Using Progress<T> means the code is not coupled to a specific UI framework (i.e. the call to BeginInvoke) in much the same way that BackgroundWorker facilitates decoupling from a specific UI framework. If you don't care, then you don't need to introduce the added complexity of using Progress<T>

As to LongRunning, as Stephen Toub says: "You'd typically only use LongRunning if you found through performance testing that not using it was causing long delays in the processing of other work" so, if you find you need to use it, then you use it--there's the added analysis or just the "complexity" of always adding the LongRunning parameter. Not using LongRunning means the thread pool thread used for the long running operation won't be usable for other, more transient, tasks and could force the thread pool to delay starting one of these transient tasks while it starts up another thread (at least a second).

There's no attributes in the framework that specifically say that BGW (or EAP, or APM) are deprecated. So, it's up to you to decide where and when any of these things are "obsolete". BGW in particular always had a very specific usage scenario that still applies to it. You have fairly decent alternatives in .NET 4.0 and 4.5; but I don't really think BGW is "obsolete".

I'm not saying always use BackgroundWorker, I'm just saying think before you automatically deprecate BackgroundWorker, in some cases it might be a better choice.



Answer 2:

我认为这些模式(APM,EAP和BGW尤其是)在.NET 4.5过时。 的组合asyncTask.Run在各方面都优于BGW。 其实,我刚开始在我的博客系列 ,我会比较BGW到Task.Run并展示它是如何在各种情况下更麻烦; 也有一些情况下,它只是稍微繁琐,但也有它的繁琐的其他情形。

现在,无论他们是在.NET 4.0中过时的完全是另外一个问题。 从您的其他职位,你在谈论的.NET 4.0 VS2010开发,所以反向移植Microsoft.Bcl.Async是不是一种选择。 在这种情况下,无论是APM也不EAP可以IMO认为是过时的。 在这个平台上,你可以考虑Task.Factory.StartNew作为替代BGW但BGW确实有一定的优势,当谈到报告进展情况及其进展和完成事件的自动线程编组。

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



Answer 3:

我的问题是:

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

即一个疑问,要求确认或否定,如果事情在.NET 4.5已经过时了,那么我不明白为什么,以及它如何在.NET 4.0中,即与任务并行库(没有不同async/await参与和.NET 4.5支持独家)

而且我很高兴找到尼克Polyak的答案,在他的CodeProject上的文章“任务并行库和异步等待功能-使用模式的简单示例”中指出:

  • “大部分的.NET 4.5的功能已被更改为任务和BackgroundWorker 是所有,但现在已经过时 ,但您仍可能会遇到一些情况下,当有必要使用EAP模式。在这种情况下,这将是很好的生产任务对象出了EAP功能,这样你就可以安排它们并行运行或等待一些以前任务的完成等。在本节中,我们展示如何去实现它”
  • “说完任务功能进行BackgroundWorker功能largerly 过时的-你可以实现任何你需要使用一个任务而不是一个BackgroundWorker仍然有可能是你想要使用一些原因。 BackgroundWorker对团队的功能-无论是因为你的遗留代码使用它或因为最你的团队成员或你的老板喜欢它,了解它比新任务功能更好”

仍然打开看的看法有什么不同argumented点



文章来源: Wasn't it .NET 4.0 TPL that made APM, EAP and BackgroundWorker asynchronous patterns obsolete?