2

Не понимаю почему ConfigureAwait(true) может быть причиной беды?

Вот пример кода:

private async void button1_Click(object sender, EventArgs e)
{
    int result = DoSomeWorkAsync().Result; // 1
}

private async Task<int> DoSomeWorkAsync()
{
    await Task.Delay(100).ConfigureAwait(true); //2
    return 1;
}

Вот его описание, но то ли тут опечатка/кривой перевод или я чего то не понимаю. Почему дедлок?:

Клик по кнопке здесь приводит к дедлоку. UI поток стартует новый I/O поток на строке «2» и уходит в спящий режим на строке «1», ожидая завершения работы. После того как I/O поток заканчивает выполнение, оставшаяся часть метода DoSomeWorkAsync передается на выполнение вызывающему (UI) потоку. Но тот в это время находится в спящем режиме, ожидая завершения метода. Дедлок.

A K
  • 28,718
Vas Mil
  • 1,670

2 Answers2

7

Потому, что вызов

int result = DoSomeWorkAsync().Result; // 1

Заблокирует UI поток, пока таска, что внутри, полностью не завершится. А таске, чтобы завершиться вот тут

await Task.Delay(100).ConfigureAwait(true); //2
return 1; // вот тут, в продолжении

Так вот, таске, чтобы завершиться, надо прыгнуть в UI поток, но она не может прыгнуть в UI поток, так как UI поток ей же самой заблокирован выше.

То есть продолжение таски ждет, когда таска закончится, а таске, чтобы закончится, надо выполнить продолжение. На лицо дедлок.

tym32167
  • 32,857
5

Лично я обьяснение @tym32167 не понял. Пришлось самому запускать апликуху и вкуривать что случается...

Итак...


Асинхронный вызов метода:

int result = await DoSomeWorkAsync();

Синхронный вызов асинхронного метода:

int result = DoSomeWorkAsync().Result; 
// то есть мы ждем пока не будет
// готового результата

Теперь смотрим на нутрянку метода DoSomeWorkAsync():

await Task.Delay(100).ConfigureAwait(true);
return 1;

.ConfigureAwait(true) -- конфигурация самой таски. Конфигурирование таски делается из UI потока, а не изнутри самой таски.

Дэдлок выходит по той причине, что перед компьютером поставлено одновременно 2 противоречивые задачи:

  • не выходить в основной поток [что бы получить результат выполнения синхронного вызова метода]
  • (внутри метода) выйти в основой поток [что бы изменить конфигурацию таски]
  • 2
    @tym32167 - прости что написал +- то же самое что и ты... просто я тебя не вкурил вооще.... А значит и он не вкурил бы, скорее всего.... – Andrew Stop_RU_war_in_UA Dec 13 '18 at 23:36
  • 1
    А что в моем ответе вызвало у вас трудности? Вы же описали ровно то же самое, что и я. – tym32167 Dec 13 '18 at 23:37
  • 1
    :) никаких проблем, пусть у автора вопроса будет выбор, два ответа лучше, чем один – tym32167 Dec 13 '18 at 23:38
  • 2
    когда я прочитал ответ - я просто не понял что конкретно там написано. Перечитал еще раз -- все равно не понял. До меня дошло что там написано только после того как сам поигрался с кодом в вижуалке) Поэтому решил расписать отдельно то же самое. Думаю здесь вопрос не неправильности твоего ответа как такового... Просто недостаточно понятная формулировка обьяснения :) По крайней мере для меня лично. А править твой ответ моими словами просто не решился -- неправильно это как-то. – Andrew Stop_RU_war_in_UA Dec 13 '18 at 23:41
  • 2
    Ага, наверное, надо было подробней расписать, спасибо, учту на будущее. – tym32167 Dec 13 '18 at 23:43
  • @tym32167, у вас не указано почему "Так вот, таске, чтобы завершиться, надо прыгнуть в UI поток". А в этом ответе, про это уже немного сказано. Хотя мне всё равно не до конца понятно, но это только потому что я ещё не прочитал\перечитал документацию .ConfigureAwait() – 4per Dec 14 '18 at 01:46
  • Оба ответа правильные :) вообще await чаще скрытое зло, лучше явно использовать Task, как по мне. Вариантов контроля намного больше. – NewView Dec 14 '18 at 03:01