【问题标题】:What is the difference between these following async methods?以下这些异步方法有什么区别?
【发布时间】:2014-12-19 14:50:12
【问题描述】:

实际上,我在理解异步原理方面遇到了一些问题。有人可以告诉我以下代码示例之间的区别吗?如果有人认为,这个例子是完全错误的,这家伙能给我纠正吗?

这是我的代码:

private async void DoHardStuffAsync()
    {
        var result = DoHardStuff();
        var secondResult = DoHardStuff();
        var thirdResult =  DoHardStuff();

        await Task.WhenAll(result, secondResult, thirdResult);

        MessageBox.Show(result.Result + secondResult.Result + thirdResult.Result);
    }

    private Task<string> DoHardStuff()
    {
        return Task.Run(() =>
        {
            var time = DateTime.Now;
            while (DateTime.Now.Subtract(time).Milliseconds < 900)
            { }

            return "finished";
        });
    }

还有这个:

private async void DoHardStuffAsync()
    {
        var result = DoHardStuff();
        var secondResult = DoHardStuff();
        var thirdResult =  DoHardStuff();

        MessageBox.Show(await result + await secondResult + await thirdResult);
    }

为什么 async != 并行?我应该在什么情况下使用异步以及我应该在什么情况下使用并行(例如任务、线程)?

【问题讨论】:

    标签: c# asynchronous parallel-processing async-await task


    【解决方案1】:

    并行和异步是两种不同形式的并发。并行性使用多个线程(例如,用于 CPU 绑定代码)。异步使用多个操作,但不一定使用多个线程(例如,对于 I/O 绑定代码)。

    Task.Run 是这两个世界之间的桥梁。它启动(可能受 CPU 限制)在后台线程上运行的代码,并返回一个允许调用线程异步处理该工作的任务。

    虽然Task.Run 可以用于基本并行,但如果您有真正的 CPU 密集型工作要做,您最好使用Parallel 或 Parallel LINQ。

    关于您的代码示例,它们都非常相似:启动了三个后台任务,调用线程异步等待它们全部完成。

    第一个确实调用Task&lt;T&gt;.Result,我不鼓励,因为如果有一些异常,那么Result 将异常包装在AggregateException 中,而await 直接引发异常。 AggregateException 使错误处理复杂化。

    第二个在每个任务上单独调用await,这还可以,但 IMO 并不理想。我认为await Task.WhenAll(..) 方法的意图更清晰(而且效率也更高一点)。

    所以,我建议结合这些方法:

    private async Task DoHardStuffAsync()
    {
      var result = DoHardStuff();
      var secondResult = DoHardStuff();
      var thirdResult =  DoHardStuff();
    
      await Task.WhenAll(result, secondResult, thirdResult);
    
      MessageBox.Show(await result + await secondResult + await thirdResult);
    }
    

    我还将返回类型更改为Task。作为一般规则,您应该avoid async void,正如我在 MSDN 文章中所描述的那样。

    DoHardStuff 的实现也有点问题。一般来说,您应该使用Task.Run调用一个方法,而不是作为一个方法的实现。我有一个blog post,详细介绍了这个主题。

    【讨论】:

    • 您说过我应该避免将异步与 void 结合使用。但是在您的博客文章中,您也这样做了吗?为什么,这只是一个坏例子,还是另一种情况?
    • 好问题!指导方针是避免async void。该指南的例外情况是语言强制您使用async void - 特别是对于事件处理程序。事件处理程序必须是async void;如果他们是async Task,他们将不会编译。
    【解决方案2】:

    这些方法出于实用性的原因相似。但是,在我看来,您对 Task.Result 的调用是不同的。这假设操作已完成并获得任务的结果。这样做非常危险,因为它可能会爆炸,所以最好使用 await

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-10
      • 2020-09-11
      • 2018-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多