繁体   English   中英

单行异步方法SynchronizationContext

[英]One-line async method SynchronizationContext

我准备了WinForms应用程序,以测试单行异步方法是否会导致死锁。 button1_Click事件等待单行异步代理方法等待的GetZero任务。 但是,它导致死锁。 为什么? 我已经读过,单行异步方法不需要在await完成后继续执行任何操作,因此没有委托可以将消息发送到导致死锁的消息泵。

作为参考, button2_Click事件在没有代理调用方的情况下等待任务GetZero的结果,并且应用程序可以正常工作。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var zero = ProxyCallery().Result;
        label1.Text += $"{zero}";
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var zero = GetZero().Result;
        label1.Text += $"{zero}";
    }

    private async Task<int> ProxyCallery()
    {
        return await GetZero();
    }

    private async Task<int> GetZero()
    {
        await Task.Delay(100).ConfigureAwait(false);

        return await Task.FromResult(0);
    }
}

为什么button1_Click导致死锁?

await Task.Delay(100).ConfigureAwait(false); 只为该呼叫配置等待。 它不会影响可能依赖于该特定await GetZero() ,例如ProxyCallery()方法中的ProxyCallery()

后一种等待仍然需要UI线程中的继续,您已使用ProxyCallery().Result阻止了该线程。 因此陷入僵局。

我已经读过,单行异步方法不需要在等待完成后继续执行任何操作,因此没有委托可以将消息发送到导致死锁的消息泵。

我不知道你在哪里读的书,但这是错误的。 编译器不会尝试优化“ tail awaits”。 实际上,即使方法中的最后一件事是await ,仍然有继续执行的代码。 至少,解开任何异常,还要将延续传播到该async方法表示的Task

因此,对于断定方法的await语句,与在方法的其他任何地方找到的await语句相比, await死锁或异步执行的任何其他方面完全没有区别。

您的ProxyCallery实际上是两行,就异步操作之后的意义而言, 连续的:

private async Task<int> ProxyCallery()
{
    var zero = await GetZero();
    return zero; // <-- continuation!
}

继续是返回任务的结果

当前,上述延续与Task.Delay任务的延续不在同一同步上下文中,这是导致死锁的原因。

您创建的每个任务都应在相同的同步上下文中!

private async Task<int> ProxyCallery()
{
    var zero = await GetZero().ConfigureAwait(false);
    return zero;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM