Почему я не могу получить исключение из асинхронного кода?
Всюду, где я читаю, говорится, что следующий код должен работать, но это не так.
public async Task DoSomething(int x)
{
try
{
// Asynchronous implementation.
await Task.Run(() => {
throw new Exception();
x++;
});
}
catch (Exception ex)
{
// Handle exceptions ?
}
}
Тем не менее, я ничего не поймаю и получаю "необработанное исключение", исходящее из линии "бросок". Я здесь невежественный.
Ответы
Ответ 1
У вас включен параметр "Только мой код". При этом он рассматривает исключение, необработанное по отношению к "только вашему коду", потому что другой код перехватывает исключение и набивает его внутри Задачи, а затем возвращается к вызову await
и поймает ваш catch выражение.
Не будучи прикрепленным в отладчике, оператор catch будет запущен, и он будет работать, как вы ожидаете. Или вы можете просто продолжить работу внутри отладчика, и он будет работать как ожидалось.
Лучше всего просто отключить "Только мой код". ИМО, это вызывает больше путаницы, чем того стоит.
Ответ 2
Ваш код даже не будет компилироваться в настоящий момент, так как оператор x++;
недоступен. Всегда обращайте внимание на предупреждения.
Однако, после исправления этого, он отлично работает:
using System;
using System.Threading.Tasks;
class Test
{
static void Main(string[] args)
{
DoSomething(10).Wait();
}
public static async Task DoSomething(int x)
{
try
{
// Asynchronous implementation.
await Task.Run(() => {
throw new Exception("Bang!");
});
}
catch (Exception ex)
{
Console.WriteLine("I caught an exception! {0}", ex.Message);
}
}
}
Вывод:
I caught an exception! Bang!
(Обратите внимание: если вы попробуете приведенный выше код в приложении WinForms, у вас будет тупик, потому что вы будете ждать выполнения задачи, которая должна была вернуться к потоку пользовательского интерфейса. Мы в порядке в консольном приложении поскольку задача будет возобновлена в потоке threadpool.)
Я подозреваю, что проблема - это просто вопрос отладки - отладчик может считать его необработанным, даже если он обрабатывается.
Ответ 3
Как сказал Слэкс, ваш код отлично работает.
Я сильно подозреваю, что вы упростили свой пример и имеете async void
в своем коде.
Следующий работает отлично:
private static void Main(string[] args)
{
CallAsync();
Console.Read();
}
public static async void CallAsync()
{
try
{
await DoSomething();
}
catch (Exception)
{
// Handle exceptions ?
Console.WriteLine("In the catch");
}
}
public static Task DoSomething()
{
return Task.Run(() =>
{
throw new Exception();
});
}
Следующий не работает:
private static void Main(string[] args)
{
CallAsync();
Console.Read();
}
public static void CallAsync()
{
try
{
DoSomething();
}
catch (Exception)
{
// Handle exceptions ?
Console.WriteLine("In the catch");
}
}
public static async void DoSomething()
{
await Task.Run(() =>
{
throw new Exception();
});
}
См. http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Асинхронные методы имеют разную семантику обработки ошибок. Когда исключение выбрасывается из задачи async Task или async Task, что исключение захватывается и помещается в объект Task. С асинхронным методов нет объекта Task, поэтому любые исключения, исключенные из метод async void будет поднят непосредственно на SynchronizationContext, который был активен, когда метод async void начал. Рисунок 2 иллюстрирует, что исключения, выброшенные из асинхронного методы не могут быть пойманы естественным путем.
Ответ 4
Вместо использования await
, войдите в свойство Task.Result
и поместите туда try
и catch
. Вы также можете следовать примеру здесь и попробовать этот стиль.
Имейте в виду, что все исключения, брошенные в контексте потока задач, завернуты в AggregateException
.
Ответ 5
Исключением не является cuaght.
Причина в том, что - когда выполняется инструкция ниже
await Task.Run(() => {
throw new Exception("Bang!");
});
его на отдельном потоке. Исключение, возникающее в этом потоке, нечеткое.
измените его так, как показано ниже
await Task.Run(() => {
try
{
throw new Exception("Bang!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});