Подтвердить что ты не робот

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

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

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

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

    return true;
}

Все, что мне нужно сделать для создания тупика, вызывает этот метод async в обработчике событий 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!
}

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

4b9b3361

Ответ 1

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

Два решения. Сначала сделайте кнопку click async тоже.

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) значение false.

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

    return true;
}

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