[英]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
語句的線程。
在第一種方法中,當awaited
的Task
完成時,將消息發布到線程池 ,該線程池運行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.