Не понимаю, как работает await и Task
Это код, вызывающий диалоговое окно в Avalonia:
public async Task<TResult> ShowDialogAsync<TResult>()
{
return await GetWindow().ShowDialog<TResult>(m_target);
}
И он работает успешно. Я попытался избавиться от async-await:
public TResult ShowDialog<TResult>()
{
var result = GetWindow().ShowDialog<TResult>(m_target);
return result.Result;
}
Но диалоговое окно не отрисовывается, и закрыть его становится невозможно. Видимо, происходит взаимная блокировка. И как это обойти?
Ответы (2 шт):
А зачем вы хотите ухудшить код?
public async Task<TResult> ShowDialogAsync<TResult>()
{
return await GetWindow().ShowDialog<TResult>(m_target);
}
В этом коде в момент вызова await
поток освобождается и UI
может обрабатывать события - отрисовывать окна, обрабатывать ввод пользователя, рисовать что-то, всё замечательно. Как только диалоговое окно закроется, будет получен и возвращён результат работы.
public TResult ShowDialog<TResult>()
{
var result = GetWindow().ShowDialog<TResult>(m_target);
return result.Result;
}
Здесь поток блокируется в момент ожидания результата работы задачи result.Result
, управление никуда не передаётся. Причём, видимо, это был поток UI
, который в любой системе ровно один. Таким образом UI
"висит", не может обрабатывать диалог, который вы хотели показать, программа "намертво повисла".
Асинхронность при работе с UI
- это прекрасно, пользуйтесь ей, не нужно от неё избавляться.
Дополнение - вариант от aepot.
Если await
- последний и единственный в методе, а также не находится в блоке try-catch
или using
, можно просто убрать и async
и await
не затронув поведение метода. Вы просто пробрасываете выше Task
, который возвращает вызываемый в коде метод.
public Task<TResult> ShowDialogAsync<TResult>()
{
return GetWindow().ShowDialog<TResult>(m_target);
}
Или, если использовать современную версию C#
для однострочного кода:
public Task<TResult> ShowDialogAsync<TResult>() => GetWindow().ShowDialog<TResult>(m_target);
Оставлю тут:
Если присмотреться к исходнику: то при вызове ShowDialog
формируется TaskCompletionSource
, которая подписывается на событие закрытия формы и ждет его выполнения. А само открытие и формирование формы происходит в потоке UI
.
Если вызвать Task.Result
то произойдет блокировка вызывающего потока UI
и форма не успеет отобразится, а событие закрытия окажется в подвешенном состоянии.
Как говорилось выше, без асинхронности здесь никак. Сама логика вызова диалогового окна подразумевает блокировку основного окна, вывода диалога, получения TResult
/DialogResult
при закрытии диалога и возврат управления на основное окно.