简体   繁体   English

单行异步方法SynchronizationContext

[英]One-line async method SynchronizationContext

I prepared WinForms application to test if a one-line async method would cause a deadlock. 我准备了WinForms应用程序,以测试单行异步方法是否会导致死锁。 button1_Click event waits for GetZero task awaited by one-line async proxy method. button1_Click事件等待单行异步代理方法等待的GetZero任务。 However, it causes deadlock. 但是,它导致死锁。 Why? 为什么? I've read that one-line async method do not need to continue anything after await completes, so there is no delegate to post to message pump causing deadlock. 我已经读过,单行异步方法不需要在await完成后继续执行任何操作,因此没有委托可以将消息发送到导致死锁的消息泵。

For reference, button2_Click event waits for the result of task GetZero without proxy caller, and application works fine. 作为参考, 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);
    }
}

Why is button1_Click causing deadlock? 为什么button1_Click导致死锁?

await Task.Delay(100).ConfigureAwait(false); configures the await only for that call. 只为该呼叫配置等待。 It doesn't affect awaits that might depend on that particular one, such as the await GetZero() in the ProxyCallery() method. 它不会影响可能依赖于该特定await GetZero() ,例如ProxyCallery()方法中的ProxyCallery()

That latter await is still going to require continuation in the UI thread, which you've blocked with ProxyCallery().Result . 后一种等待仍然需要UI线程中的继续,您已使用ProxyCallery().Result阻止了该线程。 Hence the deadlock. 因此陷入僵局。

I've read that one-line async method do not need to continue anything after await completes, so there is no delegate to post to message pump causing deadlock. 我已经读过,单行异步方法不需要在等待完成后继续执行任何操作,因此没有委托可以将消息发送到导致死锁的消息泵。

I don't know where you've read that, but it's false. 我不知道你在哪里读的书,但这是错误的。 The compiler doesn't try to optimize "tail awaits". 编译器不会尝试优化“ tail awaits”。 In reality, even if the last thing in the method is an await , there is still code to be executed on the continuation. 实际上,即使方法中的最后一件事是await ,仍然有继续执行的代码。 At the very least, unwrapping any exception, but also propagating the continuation to the Task represented by that async method. 至少,解开任何异常,还要将延续传播到该async方法表示的Task

So there is no difference at all with respect to deadlock potential, or any other aspect of asynchronous execution, for await statements that conclude a method as compared to await statements found anywhere else in a method. 因此,对于断定方法的await语句,与在方法的其他任何地方找到的await语句相比, await死锁或异步执行的任何其他方面完全没有区别。

Your ProxyCallery is actually two lines, in the sense that there is a continuation after the async operation: 您的ProxyCallery实际上是两行,就异步操作之后的意义而言, 连续的:

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

The continuation is to return the task's result ! 继续是返回任务的结果

Currently, the above continuation is not in the same synchronisation context as the continuation to the Task.Delay task, which is what causes the deadlock. 当前,上述延续与Task.Delay任务的延续不在同一同步上下文中,这是导致死锁的原因。

Every task you create should be in the same synchronisation context! 您创建的每个任务都应在相同的同步上下文中!

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