将async / await与Result混合

时间:2016-07-28 17:14:09

标签: c# async-await task

让我先简单介绍一下这个问题:

  1. 我读了几个SO问题,说你应该这样做(例如How to safely mix sync and async code
  2. 我再次阅读Async/Await - Best Practices in Asynchronous Programming说你不应该这样做
  3. 所以我知道这不是最佳做法,也不需要有人告诉我这样做。这更像是“为什么会这样”的问题。

    有了这个问题,这是我的问题:

    我写了一个小的GUI应用程序,它有2个按钮和一个状态标签。其中一个按钮将在100%的时间内重现同步和异步的死锁问题。另一个按钮调用相同的异步方法,但它包含在一个任务中,这个工作。我知道这不是一个好的编码实践,但我想理解为什么它没有相同的死锁问题。这是代码:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        private async Task<string> DelayAsync()
        {
            await Task.Delay(1000);
            return "Done";
        }
    
        private void buttonDeadlock_Click(object sender, EventArgs e)
        {
            labelStatus.Text = "Status: Running";
    
            // causes a deadlock because of mixing sync and async code
            var result = DelayAsync().Result;
            // never gets here
            labelStatus.Text = "Status: " + result;
        }
    
        private void buttonWorking_Click(object sender, EventArgs e)
        {
            labelStatus.Text = "Status: Running";
            string result = null;
    
            // still technically mixes sync and async, but works, why?
            result = Task.Run(async () =>
            {
                return await DelayAsync();
            }).Result;
    
            labelStatus.Text = "Status: " + result;
        }
    }
    

2 个答案:

答案 0 :(得分:12)

它有效,因为buttonWorking_Click异步代码(DelayAsync以及传递给async的{​​{1}} lambda)没有当前Task.Run,而SynchronizationContext异步代码(buttonDeadlock_Click)可以。您可以通过在调试器中运行并观察DelayAsync来观察差异。

我在博文Don't Block on Async Code中解释了死锁情景背后的细节。

答案 1 :(得分:9)

情景一:你坐在办公桌前。有一个收件箱。它是空的。一张纸突然到达收件箱中描述任务。你跳起来,开始跑来跑去做任务。但是任务是什么?它说要做以下事情:

  • 将白板改为&#34;运行&#34; - 好的,你那样做。
  • 将闹钟设置为一小时后。好的,你做到了。
  • 创建一张新纸,上面写着&#34;当闹钟响起时,在白板上写下DONE字样&#34;。把它放在你的收件箱里。你做到了。
  • 在白板上写下DONE字样之前别做其他事。
  • 回到办公桌前,等待下一个任务到达收件箱。

此工作流程会阻止您完成工作,因为最后两个步骤的顺序错误。

情景二:你坐在办公桌前。有一个收件箱。它是空的。一张纸突然到达收件箱中描述任务。你跳起来,开始跑来跑去做任务。但是任务是什么?它说要做以下事情:

  • 将白板改为&#34;运行&#34; - 好的,你那样做。
  • 在下一个小隔间给另一张纸给Debbie。好的,你做到了。
  • 除非有人告诉您子任务已完成,否则不执行任何操作。
  • 当发生这种情况时,请在白板上写下DONE一词。
  • 回到你的办公桌。

你给Debbie说的那张纸怎么样?它说:

  • 将闹钟设置为一小时后。好的,她做到了。
  • 当闹钟响起时,在收件箱中放一张纸说要告诉Middas你已经完成了。

这个工作流程仍然是可怕的,因为(1)你在等待黛比的闹钟熄灭时你坐在那里什么都不做,(2)你在浪费时间两个工人,当你可以有一个工人做所有的工作。工人很贵。

但是,此工作流程不会阻止您完成最终的工作。它没有死锁,因为你没有等待你将来要做的工作,你正在等待别人做这项工作。

(我注意到这不是你的程序中发生的事情的完全类比,但它足够接近这个想法。)