Можете ли вы объяснить, почему исключение не перехватывается, если я не жду асинхронной задачи?

Начиная с проблемы, которая возникла у меня в коде, я создал это простое приложение, чтобы воссоздать проблему:

private async void button1_Click(object sender, EventArgs e)
{
    Task task = Task.Run(() =>
    {
        TestWork();
    });
    try
    {
        await task;
        MessageBox.Show("Exception uncaught!");
    }
    catch (Exception) { MessageBox.Show("Exception caught!"); }
}

private async void button2_Click(object sender, EventArgs e)
{
    Task task = TestWork();
    try
    {
        await task;
        MessageBox.Show("Exception uncaught!");
    }
    catch (Exception) { MessageBox.Show("Exception caught!"); }
}

private async Task TestWork()
{
    throw new Exception();
}

Код для button1_Click не поймает исключение. Я убедился, что это потому, что я не жду асинхронного метода TestWork. Действительно, у меня есть предупреждающее сообщение от Visual Studio, которое сообщает мне, что я не жду метода. Однако решение компилируется, и я боюсь, что это может произойти где-то еще в моем коде, если я широко использую async/await. Итак, не могли бы вы объяснить причину и указать какое-то золотое правило, чтобы избежать этого?

P.S.: Работает, если в коде для button1_Click написать:

Task task = Task.Run(async () =>
{
    await TestWork();
});

person Mauro Ganswer    schedule 05.06.2012    source источник
comment
Другой способ правильно написать код для button1_Click — это Task.Run(() => TestWork()).   -  person svick    schedule 05.06.2012


Ответы (1)


В вашей первой реализации button1_Click вы игнорируете результат Task, возвращаемый TestWork.

В вашей модифицированной версии await проверяет наличие исключения и распространяет его для вас.

На самом деле предупреждение компилятора, которое вы видите, обычно важно. Если TestWork на самом деле запустил что-то в другом потоке, то, поскольку оболочка Task в вашей первой реализации не ждет завершения TestWork, она просто завершится, как только запустится TestWork.

Написав так, становится понятнее:

Task task = Task.Run( () =>
    {
        Task t = TestWork();
        // ignore t
    }
);
person Nick Butler    schedule 05.06.2012
comment
@Adriano То, что метод async, не означает, что он будет работать в пуле потоков! На самом деле есть две причины, по которым TestWork() там не запустится: 1. Он не содержит await, а значит, будет работать полностью синхронно. 2. Он выполняется в потоке пользовательского интерфейса, что означает, что любой код после await будет выполняться в потоке пользовательского интерфейса, а не в пуле. - person svick; 05.06.2012
comment
Я не уверен, что неиспользуемая переменная понятнее, вам нужен комментарий в обоих случаях. - person svick; 05.06.2012
comment
@ Адриано Ты совершенно не прав. async ведет себя совсем не так, я думаю, вам следует сначала прочитать об этом больше, прежде чем делать подобные заявления. Если вы мне не верите или кому-то из команды C#, попробуйте сами. - person svick; 05.06.2012
comment
@svick ты прав (кто сказал, что я тебе не верю?), спасибо за поясняющую ссылку!!! - person Adriano Repetti; 05.06.2012