ConfigureAwait
настраивает поведение ключевого слова await
в том же выражении. Он не влияет на другие операторы await
в других методах и не действует, если вы не используете await
.
Он определяет, захватывает ли оператор await
текущий SynchronizationContext (если он есть). На практике, если вы выполняете оператор await
в потоке пользовательского интерфейса, await task;
будет запускать код после await
в потоке пользовательского интерфейса, тогда как await task.ConfigureAwait(false)
будет запускать код после await
в потоке ThreadPool.
В вашем первом примере:
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
.
В первом методе, когда Task
, являющийся awaited
, завершается, сообщение отправляется в пул потоков , который выполняет оператор return
.
Во втором методе оператор await
захватывает текущий SynchronizationContext (который относится к потоку пользовательского интерфейса) и использует его для запуска оператора return
. Это означает, что по прошествии 3 секунд сообщение отправляется в поток пользовательского интерфейса, сообщая ему о необходимости выполнения оператора return
.
В вашем первом фрагменте, который вызывает GetStringAsync
:
var task = GetStringAsync().ConfigureAwait(false).GetAwaiter();
Вызов ConfigureAwait
здесь ничего не делает, потому что вы не await
получаете результат. Вы можете удалить его без изменений.
var result = task.GetResult(); // deadlock
Поток пользовательского интерфейса необходим для запуска оператора return
в GetStringAsync
. Поскольку вы заблокировали его при вызове GetResult()
, он не может завершить метод GetStringAsync
, и поэтому у вас тупик.
Во втором фрагменте, который вызывает GetStringWithInnerCallConfigureAwaitFalseAsync
:
var task = GetStringWithInnerCallConfigureAwaitFalseAsync().ConfigureAwait(false).GetAwaiter();
Опять же, вызов ConfigureAwait(false)
ничего не делает, потому что вы не await
получаете результат.
var result = task.GetResult(); // No deadlock
На этот раз GetStringWithInnerCallConfigureAwaitFalseAsync
делает await ...ConfigureAwait(false)
, и поэтому код после await
запускается в пуле потоков, а не в потоке пользовательского интерфейса. Поэтому поток пользовательского интерфейса не требуется для завершения этого метода, и поэтому вы можете безопасно (!) Заблокировать его.
Button11.Content = result; // No crash
Вы вызвали этот метод в потоке пользовательского интерфейса, и вы никогда его не перемещали - вы вызываете все синхронно, нет await
с и т. Д. Поэтому вы все еще находитесь в потоке пользовательского интерфейса на данный момент