无法继续使用捕获的 GUI 上下文,但为什么会出现死锁?

我想知道为什么在以下情况下没有继续使用捕获的 GUI 上下文时会出现死锁。


public Form1()

{

    InitializeComponent();

    CheckForIllegalCrossThreadCalls = true;

}


async Task DelayAsync()

{

    // GUI context is captured here (right before the following await)

    await Task.Delay(3000);//.ConfigureAwait(false);

    // As no  code follows the preceding await, there is no continuation that uses the captured GUI context. 

}


private async void Button1_Click(object sender, EventArgs e)

{

    Task t = DelayAsync();


    t.Wait();

}

编辑:

我知道僵局可以通过任何一种方式解决

  • 使用await Task.Delay(3000).ConfigureAwait(false);

  • 替换t.Wait();await t;.

但这不是问题。问题是

为什么没有继续使用捕获的 GUI 上下文时会出现死锁?在我的心智模型中,如果有继续,那么它将使用捕获的 GUI 上下文,因此会导致死锁。


幕布斯6054654
浏览 56回答 1
1回答

哆啦的时光机

async与服务员一起工作,而不是与任务一起工作。因此,在方法末尾需要一些额外的逻辑来将等待者的状态转换为任务。您认为没有延续的假设是错误的。如果您刚刚返回任务,那将是正确的:Task DelayAsync(){&nbsp; &nbsp; return Task.Delay(3000);}但是,当您将方法标记为 时,事情会变得更加复杂async。方法的一个重要属性async是它处理异常的方式。例如考虑这些方法:Task NoAsync(){&nbsp; &nbsp; throw new Exception();}async Task Async(){&nbsp; &nbsp; throw new Exception();}现在如果你调用它们会发生什么?var task1 = NoAsync(); // Throws an exceptionvar task2 = Async(); // Returns a faulted task不同之处在于异步版本将异常包装在返回的任务中。它与我们的案例有什么关系?当您await使用方法时,编译器实际上会调用GetAwaiter()您正在等待的对象。等待者定义了 3 个成员:该IsCompleted物业OnCompleted方法_GetResult方法_可以看到,没有成员直接返回异常。如何知道服务员是否有故障?要知道这一点,您需要调用GetResult将抛出异常的方法。回到你的例子:async Task DelayAsync(){&nbsp; &nbsp; await Task.Delay(3000);}如果Task.Delay抛出异常,async机器需要将返回任务的状态设置为故障。要知道是否Task.Delay抛出异常,需要在完成GetResult后调用等待Task.Delay者。因此你有一个延续,虽然在看到代码时并不明显。在幕后,异步方法看起来像:Task DelayAsync(){&nbsp; &nbsp; var tcs = new TaskCompletionSource<object>();&nbsp; &nbsp; try&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var awaiter = Task.Delay(3000).GetAwaiter();&nbsp; &nbsp; &nbsp; &nbsp; awaiter.OnCompleted(() =>&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // This is the continuation that causes your deadlock&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; try&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; awaiter.GetResult();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tcs.SetResult(null);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; catch (Exception ex)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tcs.SetException(ex);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; }&nbsp; &nbsp; catch (Exception ex)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; tcs.SetException(ex);&nbsp; &nbsp; }&nbsp; &nbsp; return tcs.Task;}实际代码比较复杂,用aAsyncTaskMethodBuilder<T>代替了TaskCompletionSource<T>,但是思路是一样的。
打开App,查看更多内容
随时随地看视频慕课网APP