异步/等待VS的BackgroundWorker(Async/await vs Background

2019-06-18 04:29发布

在过去的几天里,我已经测试过的.NET 4.5和C#5的新功能。

我喜欢它的新的异步/等待功能。 早些时候,我曾使用的BackgroundWorker与响应UI背景,以处理更长的过程。

我的问题是:这些具有不错的新功能后,我什么时候应该使用异步/等待,当一个BackgroundWorker的 ? 这是两个常见的场景?

Answer 1:

异步/ AWAIT被设计为替代的构建体,如BackgroundWorker 。 而你,如果你想当然可以使用它,你应该能够使用异步/ AWAIT,与其他几个TPL工具一起,以处理一切,是在那里。

由于这两个工作,把它归结为个人喜好为你使用的时候。 什么是更快? 什么是更容易理解吗?



Answer 2:

这可能是TL; DR为多,但是,我觉得比较awaitBackgroundWorker是喜欢上了这后续比较苹果和桔子和我的想法:

BackgroundWorker是指到你想要在后台执行,在一个线程池线程单任务模型。 async / await是异步操作异步等待语法。 这些行动可能会或可能不会使用线程池线程甚至可以使用任何其他线程 。 所以,他们是苹果和桔子。

例如,你可以这样做有以下await

using (WebResponse response = await webReq.GetResponseAsync())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        int bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
    }
}

但是,可能你不会模型,在后台工作,你很可能做这样的事在.NET 4.0(之前await ):

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

请注意比较了两种语法,以及如何你不能使用的处置不相交usingasync / await

但是,你不会做这样的事情与BackgroundWorkerBackgroundWorker通常是造型,你不想影响UI响应一个长时间运行的操作。 例如:

worker.DoWork += (sender, e) =>
                    {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                    };
worker.RunWorkerCompleted += (sender, eventArgs) =>
                                {
                                    // TODO: do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

没什么好说的,那里可以使用异步/等待着, BackgroundWorker是创建线程你。

现在,你可以使用第三方物流来代替:

var synchronizationContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
                      {
                        int i = 0;
                        // simulate lengthy operation
                        Stopwatch sw = Stopwatch.StartNew();
                        while (sw.Elapsed.TotalSeconds < 1)
                            ++i;
                      }).ContinueWith(t=>
                                      {
                                        // TODO: do something on the UI thread, like
                                        // update status or display "result"
                                      }, synchronizationContext);

在这种情况下TaskScheduler中创建线程你(假设默认TaskScheduler ),并且可以利用await如下:

await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                        ++i;
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

在我看来,一个重要的比较,无论您是报告进展情况或没有。 例如,你可能有一个BackgroundWorker like这样的:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.ProgressChanged += (sender, eventArgs) =>
                            {
                            // TODO: something with progress, like update progress bar

                            };
worker.DoWork += (sender, e) =>
                 {
                    int i = 0;
                    // simulate lengthy operation
                    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) =>
                                {
                                    // do something on the UI thread, like
                                    // update status or display "result"
                                };
worker.RunWorkerAsync();

但是,你不会处理一些这一点,因为你会拖和拖放到窗体的设计表面的背景工作者组成部分-这是你不能做async / awaitTask ...即你会不会手动创建对象,设置属性,设置事件处理程序。 你只填写的身体DoWorkRunWorkerCompletedProgressChanged事件处理程序。

如果“转换”,为异步/ AWAIT,你会做这样的事情:

     IProgress<int> progress = new Progress<int>();

     progress.ProgressChanged += ( s, e ) =>
        {
           // TODO: do something with e.ProgressPercentage
           // like update progress bar
        };

     await Task.Factory.StartNew(() =>
                  {
                    int i = 0;
                    // simulate lengthy operation
                    Stopwatch sw = Stopwatch.StartNew();
                    while (sw.Elapsed.TotalSeconds < 1)
                    {
                        if ((sw.Elapsed.TotalMilliseconds%100) == 0)
                        {
                            progress.Report((int) (1000 / sw.ElapsedMilliseconds))
                        }
                        ++i;
                    }
                  });
// TODO: do something on the UI thread, like
// update status or display "result"

如果没有对一个设计师表面拖动一个组件的能力,它是真正由读者来决定,这是“更好”。 但是,这对我来说,是的比较awaitBackgroundWorker ,不是你是否可以等待内置的方法,比如Stream.ReadAsync 。 例如,如果你使用BackgroundWorker如预期,它可能很难转换为使用await

其他的想法: http://jeremybytes.blogspot.ca/2012/05/backgroundworker-component-im-not-dead.html



Answer 3:

这是一个很好的介绍: http://msdn.microsoft.com/en-us/library/hh191443.aspx螺纹部分仅仅是你在找什么:

异步方法旨在非阻塞操作。 在异步方法的AWAIT表达等候的任务正在运行时,不会阻止当前线程。 相反,表达签约的方法的其余部分的延续,并将控制返回到异步方法的调用者。

异步和等待关键字不会导致创建额外的线程。 异步方法不需要多线程,因为异步方法不会在自己的线程中运行。 该方法对当前同步上下文运行,并使用时间,只有当该方法是激活的线程上。 您可以使用Task.Run移动CPU绑定工作提高到一个后台线程,而是后台线程不会,这只是等待结果变得可用一个过程中提供帮助。

基于异步的方法进行异步编程优选现有的方法在几乎所有情况下。 尤其是,这种方法比BackgroundWorker的IO的绑定操作更好,因为代码更简单,你不必防范竞争条件。 在与Task.Run组合,异步编程比BackgroundWorker的CPU为绑定操作更好,因为异步编程分离工作运行的代码,Task.Run转移到线程池的协调细节。



Answer 4:

BackgroundWorker的明确标记为过时在.NET 4.5:

  • 在这本书由约瑟夫阿尔巴哈利,本阿尔巴哈利“C#5.0果壳中的权威参考”
  • 斯蒂芬·克利里的回答我的问题“是不是它.NET 4.0 TPL是由APM,EAP和BackgroundWorker的异步模式过时了吗?”

MSDN文章“异步编程与异步和等待(C#和Visual Basic)”讲述:

基于异步的方法进行异步编程优选现有的方法在几乎所有情况下 。 尤其是,这种方法比更好的BackgroundWorker 的IO绑定操作 ,因为代码更简单,你不必防范竞争条件。 在与Task.Run组合,异步编程优于BackgroundWorker的 对CPU绑定的操作 ,因为异步编程分离工作运行代码的协调细节Task.Run转移到线程池

UPDATE

  • 响应@埃伦- otzap的评论:
    “为IO绑定操作,因为代码更简单,你不必防范竞争条件”可以occure哪个种族的条件下,你可以举个例子? “

这个问题应该已经把作为一个单独的职位。

维基百科有一个很好的解释赛车条件。 它的必要组成部分是多线程和来自同一个MSDN文章异步编程与异步和等待(C#和Visual Basic) :

异步方法旨在非阻塞操作。 在异步方法的AWAIT表达等候的任务正在运行时,不会阻止当前线程。 相反,表达签约的方法的其余部分的延续,并将控制返回到异步方法的调用者。

异步和等待关键字不会导致创建额外的线程。 异步方法不需要多线程,因为异步方法不会在自己的线程中运行。 该方法对当前同步上下文运行,并使用时间,只有当该方法是激活的线程上。 您可以使用Task.Run移动CPU绑定工作提高到一个后台线程,而是后台线程不会,这只是等待结果变得可用一个过程中提供帮助。

基于异步的方法进行异步编程优选现有的方法在几乎所有情况下。 尤其是,这种方法比BackgroundWorker的IO的绑定操作更好,因为代码更简单,你不必防范竞争条件。 在与Task.Run组合,异步编程比BackgroundWorker的CPU为绑定操作更好,因为异步编程分离工作运行代码的协调细节Task.Run转移到线程池

也就是说,“异步和等待关键字不会导致创建额外的线程”。

至于我记得我自己的努力,当我一年前研究这篇文章,如果您已经运行,并从同一篇文章的代码示例扮演,你可以在情况磕碰,其非异步版本(你可以尝试转换它自己)无限期地阻塞!

另外,对于具体的例子,你可以搜索这个网站。 下面是一些例子:

  • 主叫用c#5.0异步方法
  • 为什么当通过TPL /任务执行该代码会失败?
  • 等待VS Task.Wait -死机?


文章来源: Async/await vs BackgroundWorker