Различная обработка исключений между Task.Run и Task.Factory.StartNew
Я столкнулся с проблемой при использовании Task.Factory.StartNew
и пытался поймать exception
это брошено. В моем приложении у меня есть долгосрочное задание, которое я хочу инкапсулировать в Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);
Тем не менее, исключение не ловится, когда я использую Task.Factory.StartNew
, Однако он работает так, как я ожидаю, когда я использую Task.Run
, который я думал, был просто оберткой на Task.Factory.StartNew
(согласно, например, этой статье MSDN).
Здесь приведен рабочий пример, с той разницей, что исключение записывается в консоль при использовании Task.Run
, но не при использовании Factory.StartNew
,
Мой вопрос будет:
если у меня есть LongRunning
задача, которая имеет возможность генерировать исключения, как я должен обрабатывать их в вызывающем коде?
private static void Main(string[] args)
{
Task<bool> t = RunLongTask();
t.Wait();
Console.WriteLine(t.Result);
Console.ReadKey();
}
private async static Task<bool> RunLongTask()
{
try
{
await RunTaskAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
Console.WriteLine("success");
return true;
}
private static Task RunTaskAsync()
{
//return Task.Run(async () =>
// {
// throw new Exception("my exception");
// });
return Task.Factory.StartNew(
async () =>
{
throw new Exception("my exception");
});
}
3 ответа
Ваша проблема в том, что StartNew
не работает как Task.Run
с async
делегаты. Тип возврата StartNew
является Task<Task>
(который конвертируется в Task
). Внешний" Task
представляет начало метода, а "внутренний" Task
представляет завершение метода (включая любые исключения).
Чтобы добраться до внутреннего Task
, ты можешь использовать Unwrap
, Или вы можете просто использовать Task.Run
вместо StartNew
за async
код. LongRunning
это просто подсказка по оптимизации и действительно необязательно. Стивен Туб имеет хороший пост в блоге о разнице между StartNew
а также Run
и почему Run
(обычно) лучше для async
код.
Обновление от @usr комментарий ниже: LongRunning
относится только к началу async
метод (до первой незавершенной операции await
ред). Так что почти наверняка лучше использовать Task.Run
в этом случае.
Я приведу некоторые из моих комментариев в ответ, потому что они оказались полезными:
LongRunning
идентично принудительному созданию нового потока на практике. И ваш асинхронный метод, вероятно, не находится в этом потоке в течение длительного времени (он отключается в первой точке ожидания). Вы не хотите LongRunning в этом случае.
Неважно, как долго работает асинхронный метод. Поток уничтожается с самого первого ожидания (который работает с незавершенной задачей).
Может ли компилятор использовать эту подсказку каким-либо образом? Компилятор, как правило, не может анализировать ваш код каким-либо существенным образом. Также компилятор ничего не знает о TPL. TPL - это библиотека. И эта библиотека просто всегда запускает новый поток. Уточнить LongRunning
если ваша задача почти всегда будет сжигать 100% ЦП в течение нескольких секунд или будет блокироваться в течение нескольких секунд с очень высокой вероятностью.
Я думаю, ты не хочешь LongRunning
здесь, потому что, если вы блокируете, почему вы используете асинхронный в первую очередь? Async - это не блокировка, а выход из потока.
Это должно быть возможно при первом развертывании задачи:
await RunTaskAsync().Unwrap();
Или в качестве альтернативы:
await await RunTaskAsync();