Winforms вызов асинхронного метода зависает программа

Я работал над этой проблемой некоторое время, но теперь я действительно хотел бы понять, что идет не так. У меня довольно простое приложение (это плагин Turtoise SVN для youtrack, но я могу воспроизвести проблему с помощью тривиального приложения winforms).

У меня есть асинхронный метод ResolveIssue

public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
    await Task.Delay(1000);

    return true;
}

Все, что мне нужно сделать, чтобы создать тупик, это вызвать этот асинхронный метод в Button обработчик события и вызов Task.Wait или же Task.Result, как это

private void buttonOk_Click(object sender, System.EventArgs e)
{
    var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
    if (asyncResolvedIssue.Result) {} // <== deadlock!
}

Теперь я понимаю, что довольно странно иметь асинхронный метод и активно его ждать, но с какой стати это может привести к тупику?!

1 ответ

Решение

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

Два решения. Сначала нажмите кнопку асинхронно.

private async void buttonOk_Click(object sender, System.EventArgs e)
{
    var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
    if (await asyncResolvedIssue) {} // <== no deadlock!
}

Обработчики событий - это единственное место, которое вам разрешено делать async void,

Другой вариант - сказать Task.Delay ему не нужно запускать остальную часть своей функции в потоке пользовательского интерфейса, установив ConfigureAwait(bool) ложно.

public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
    await Task.Delay(1000).ConfigureAwait(false);

    return true;
}

Теперь строка кода после Task.Delay будет работать в потоке потоков вместо потока пользовательского интерфейса и не будет заблокирован тем, что поток пользовательского интерфейса в настоящее время заблокирован.

Другие вопросы по тегам