簡體   English   中英

ConfigureAwait和GetAwaiter更改行為

[英]ConfigureAwait and GetAwaiter changing behaviour

我在試驗任務。 我有

private async Task<string> GetStringWithInnerCallConfigureAwaitFalseAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Finished!";
}

private async Task<string> GetStringAsync()
{
    await Task.Delay(3000);
    return "Finished!";
}

對我來說很奇怪:

private void Button10_Click(object sender, RoutedEventArgs e)
{
    Button10.Content = "GetAwaiter() GetResult() + deadlock";
    var task = GetStringAsync().ConfigureAwait(false).GetAwaiter();
    var result = task.GetResult(); // deadlock
    Button10.Content = result;
}

我期望在最后一行沒有死鎖崩潰因為非UI上下文,因為它不會使用GetAwaiter(),但我遇到了死鎖

下一個:

private void Button11_Click(object sender, RoutedEventArgs e)
{
    Button11.Content = "GetAwaiter() GetResult() No deadlock";
    var task = GetStringWithInnerCallConfigureAwaitFalseAsync().ConfigureAwait(false).GetAwaiter();
    var result = task.GetResult(); // No deadlock
    Button11.Content = result; // No crash
}

在這里,我預計由於非UI上下文而在最后一行崩潰,但它沒有問題。 當我們使用GetAwaiter()時, ConfigureAwait(false)是否有意義?

ConfigureAwait在同一表達式中配置await關鍵字的行為。 它不會影響其他方法中的其他await語句,如果不使用await則無效。

它控制await語句是否捕獲當前的SynchronizationContext(如果有)。 實際上,如果在UI線程上運行await語句,則await task; 也將在UI線程上的await 之后運行代碼,而await task.ConfigureAwait(false)將在ThreadPool線程上的await之后運行代碼。

在你的第一個例子中:

private async Task<string> GetStringWithInnerCallConfigureAwaitFalseAsync()
{
    await Task.Delay(3000).ConfigureAwait(false);
    return "Finished!"; // <-- Run on the thread pool
}

private async Task<string> GetStringAsync()
{
    await Task.Delay(3000);
    return "Finished!"; // <-- Run on the captured SynchronizationContext (if any)
}

這里的行為有所不同,那就是運行return語句的線程。

在第一種方法中,當awaitedTask完成時,將消息發布到線程池 ,該線程池運行return語句。

在第二種方法中, await語句捕獲當前的SynchronizationContext(引用UI線程),並使用它來運行return語句。 這意味着消息在3秒后發布到UI線程,告訴它運行該return語句。


在您的第一個調用GetStringAsync代碼段中:

var task = GetStringAsync().ConfigureAwait(false).GetAwaiter();

ConfigureAwait的調用在此處不執行任何操作,因為您沒有await結果。 您可以刪除它而不做任何更改。

var result = task.GetResult(); // deadlock

需要UI線程才能在GetStringAsync運行return語句。 由於您在調用GetResult()已將其阻止,因此無法完成GetStringAsync方法,因此您遇到了死鎖。


在第二個調用GetStringWithInnerCallConfigureAwaitFalseAsync代碼片段中:

var task = GetStringWithInnerCallConfigureAwaitFalseAsync().ConfigureAwait(false).GetAwaiter();

同樣,對ConfigureAwait(false)的調用什么都不做,因為你沒有await結果。

var result = task.GetResult(); // No deadlock

這次, GetStringWithInnerCallConfigureAwaitFalseAsync await ...ConfigureAwait(false) ,因此await之后的代碼在線程池而不是UI線程上運行。 因此,不需要UI線程來完成此方法,因此您可以安全地(!)阻止它。

Button11.Content = result; // No crash

你在UI線程上調用了這個方法,你從來沒有離開它 - 你同步調用所有東西,沒有await s等等。因此你現在仍然在UI線程上

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM