难道不是.NET 4.0 TPL,使得APM,EAP和BackgroundWorker的异步模式过时了吗? [英] Wasn't it .NET 4.0 TPL that made APM, EAP and BackgroundWorker asynchronous patterns obsolete?

查看:93
本文介绍了难道不是.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 的使用虽然,我希望,我的问题应该是关于其他异步模式有效。

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

异步模式:


  • A).NET 4.5使得他们过时


    • 命名为这样的书由约瑟夫阿尔巴哈利,本阿尔巴哈利C#5.0果壳中的权威参考子篇<一个href=\"http://books.google.ru/books?id=t1de8nSVYnkC&pg=PA601&lpg=PA601&dq=%22The%20Framework%20employs%20other%20patterns%20for%20asynchrony%20which%20$p$pcede%20tasks%20and%22&source=bl&ots=24skTvKo4H&sig=R546MsYR4mO7EdUeCfQVHaSNCYs&hl=en&sa=X&ei=93uAUdzvA4Ot4ATe0oGQAQ&ved=0CCwQ6AEwAA#v=onepage&q=%22The%20Framework%20employs%20other%20patterns%20for%20asynchrony%20which%20$p$pcede%20tasks%20and%22&f=false\">Obsolete模式,而它的previous版C#4.0果壳中的权威参考没

    • MSDN文章异步编程与异步和等待(C#和Visual Basic)通知:



  

基于异步的方法进行异步编程是preferable到
  在几乎所有情况下,现有的方法。特别是,这种方法
  比<更好href=\"http://msdn.microsoft.com/en-us/library/vstudio/system.componentmodel.backgroundworker.aspx\">BackgroundWorker为IO绑定操作,因为
  code是简单,你不必防范竞争条件。
  在与Task.Run组合,异步编程优于
  <一href=\"http://msdn.microsoft.com/en-us/library/vstudio/system.componentmodel.backgroundworker.aspx\">BackgroundWorker对于CPU绑定的操作,因为异步编程
  分离工作运行您code的协调细节
  这Task.Run转移到线程池


我还有疑问:


  1. 是这些模式(首先是,BGW)在.NET 4.5过时了吗?

  2. 如果他们是过时的.NET 4.5,为什么他们不过时在.NET 4.0中?

    2A)我理解错误地.NET 4.5的新功能仍然在.NET 4.0中易可执行/重现?



解决方案

我一般建议工作和/或的await 如果使用.NET 4.5。但工作&安培; BGW有2个截然不同的场景。任务是良好的,可以被链接到一个延续,等待擅长的任务编组隐回UI线程一般短异步任务。 BGW是很好的一个长的操作,应该不会影响你的用户界面的响应速度。你可以拖放一个BGW到设计表面,然后双击创建事件处理程序。你不必应付 LongRunning ConfigureAwait 如果你不想当元帅到另一个线程。许多人发现BGW比进度更容易 IProgress&LT; T&GT;

下面是一个使用的例子都在一个冗长的操作的情景:

由于这个问题特别提到.NET 4.0,以下是简单的code,它使用了工作做了一个冗长的操作,而到UI提供进度:

  startButton.Enabled = FALSE;
VAR任务= Task.Factory。
                StartNew(()=&GT;
                    {
                        的foreach(在Enumerable.Range变种×(1,10))
                        {
                            VAR进度= X * 10;
                            Thread.sleep代码(500); //假冒工作
                            的BeginInvoke((动作)委托{
                               progressBar1.Value =进展情况;
                            });
                        }
                    },TaskCreationOptions.LongRunning)
                .ContinueWith(T =&GT;
                    {
                        startButton.Enabled = TRUE;
                        progressBar1.Value = 0;
                    });

的BackgroundWorker

类似code可能是:

  startButton.Enabled = FALSE;
BackgroundWorker的BGW =新的BackgroundWorker {WorkerReportsProgress =真};
bgw.ProgressChanged + =(发件人,参数)=&GT;
    {progressBar1.Value = args.ProgressPercentage; };
bgw.RunWorkerCompleted + =(发件人,参数)=&GT;
{
    startButton.Enabled = TRUE;
    progressBar1.Value = 0;
};
bgw.DoWork + =(发件人,参数)=&GT;
{
    的foreach(在Enumerable.Range变种×(1,10))
    {
        Thread.sleep代码(500);
        ((BackgroundWorker的)寄件人).ReportProgress(X * 10);
    }
};
bgw.RunWorkerAsync();

现在,如果你使用.NET 4.5,你可以使用进度&LT; T&GT; 而不是的BeginInvoke 通话与工作。而且,由于在4.5,使用等待将可能是更具可读性:

  startButton.Enabled = FALSE;
VAR PR =新的进展和LT;诠释&GT;();
pr.ProgressChanged + =(O,I)=&GT; progressBar1.Value = I;
等待Task.Factory。
            StartNew(()=&GT;
                        {
                            的foreach(在Enumerable.Range变种×(1,10))
                            {
                                Thread.sleep代码(500); //假冒工作
                                ((IProgress&LT; INT&GT;)PR)。报道(X * 10);
                            }
                        },TaskCreationOptions.LongRunning);
startButton.Enabled = TRUE;
progressBar1.Value = 0;

使用进度&LT; T&GT; 表示code未连接到特定的UI框架(即调用的BeginInvoke )的的BackgroundWorker 从特定的UI框架有助于脱钩。如果你不关心,那么你并不需要引入使用复杂性的增加进度&LT; T&GT;

至于 LongRunning ,斯蒂芬Toub说:你通常只使用LongRunning如果你通过不使用它是导致长时间延误的处理性能的测试发现其他工作,因此,如果你发现你需要使用它,那么你使用它 - 有增加的分析,或只是总是添加 LongRunning 参数的复杂性 。不使用LongRunning装置用于长时间运行操作的线程池线程不会对其他更短暂,任务可用和可能迫使线程池延迟开始的这些瞬变任务之一,而在启动另一个线程(至少一个秒)。

有在该框架没有属性,专门说BGW(或EAP或APM)是的德precated 的。因此,它是由你来决定何时何地任何这些东西都是过时。 BGW尤其是一直有仍然适用于它一个非常具体的使用场景。你在.NET 4.0和4.5还算过得去的替代品;但我真的不认为BGW是过时。

我不是说总是用的BackgroundWorker ,我只是说的认为的之前,你会自动去precate BackgroundWorker的,在某些情况下,可能是更好的选择。

I have 2 kinds of C# WPF app projects:

  • based on .NET 4.0 that I cannot migrate to .NET 4.5
  • based on .NET 4.0 that I can migrate to .NET 4.5

All of them should spawn 2-10 long-running (days) processes which can be cancelled and re-launched by users.

I am interested to follow the best design practices. First of all, now, I am interested to disambiguate about BackgroundWorker usage though, I hope, my question should be valid about other asynchronous patterns.

I see (contradicting) concurrent points of view about

asynchronous patterns:

"The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool"

I am still in doubt:

  1. Are those patterns (first of all, BGW) obsolete in .NET 4.5 ?
  2. If they are obsolete in .NET 4.5 why aren't they obsolete in .NET 4.0?

    2A) Do I understand incorrectly that .NET 4.5 new features are still "easy" implementable/reproducible in .NET 4.0?

解决方案

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.

这篇关于难道不是.NET 4.0 TPL,使得APM,EAP和BackgroundWorker的异步模式过时了吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆