将async/await与Result混合

Mid*_*das 9 c# task async-await

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

  1. 我已经阅读了几个SO问题,说你不应该这样做(比如如何安全地混合同步和异步代码)
  2. 我已经读过Async/Await - 异步编程中的最佳实践再次说你不应该这样做

所以我知道这不是最佳做法,也不需要任何人告诉我这样做.这更像是"为什么这个工作"的问题.

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

我写了一个小的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 12

它的工作原理是因为buttonWorking_Click异步代码(DelayAsync以及async传递给的lambda Task.Run)没有电流SynchronizationContext,而buttonDeadlock_Click异步代码(DelayAsync)则没有.您可以通过在调试器中运行并观察来观察差异SynchronizationContext.Current.

我在博客文章Do not Block on Async Code中解释了死锁场景背后的细节.


Eri*_*ert 9

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

  • 将白板更改为"正在运行" - 好的,你这样做.
  • 将闹钟设置为一小时后.好的,你做到了.
  • 创建一张新纸,上面写着"当闹钟响起时,在白板上写下DONE".把它放在你的收件箱里.你做吧.
  • 在DONE写在白板上之前不要做任何其他事情.
  • 回到您的办公桌,等待下一个任务到达收件箱.

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

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

  • 将白板更改为"正在运行" - 好的,你这样做.
  • 在下一个小隔间给另一张纸给黛比.好的,你做到了.
  • 在有人告诉您子任务完成之前不要做任何事情.
  • 发生这种情况时,请在白板上写下DONE字样.
  • 回到你的办公桌.

你给Debbie的那张纸说了什么?它说:

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

这个工作流程仍然很糟糕(1)你在等待黛比的闹钟熄火时你坐在那里什么都不做,(2)当你可以让一个工人完成所有的工作时你浪费了两个工人的时间.工人很贵.

但是这个工作流程并不能阻止你最终完成工作.它不会陷入僵局,因为你不是在等待将来你将会做的工作,你正在等待别人去做这项工作.

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

  • 这正是我订阅Eric的StackOverflow RSS提要的原因.即使您知道答案,您也一定会获得额外的见解和/或向同事解释事物的好方法. (3认同)