#c# #exception #.net-core #async-await #task-parallel-library
#c# #исключение #.net-ядро #асинхронный-ожидание #задача-параллельная-библиотека
Вопрос:
У меня есть .Net core приложение, которое в основном делает что-то вроде этого:
public async Task<IOBoundStuffResult> DoIOBoundStuff()
{
// Do IO bound stuff ...
return new IOBoundStuffResult
{
Id = getIdForThing()
};
}
public async Task<OtherIOBoundStuffResult> DoOtherIOBoundStuff()
{
// Do other IO bound stuff ...
return new OtherIOBoundStuffResult
{
Uri = getUriForThing()
};
}
public async Task<IOBoundTaskResult> DoIOBoundStuff()
{
var ioBoundTask1 = doIOBoundStuff();
var ioBoundTask2 = doOtherIOBoundStuff();
return await Task.WhenAll(ioBoundTask1, ioBoundTask2)
.ContinueWith((task) =>
{
var id = ioBoundTask1.Result.Id;
var uri = ioBoundTask2.Result.Uri;
doSomethingWithIdAndUri(id, uri);
return new IOBoundTaskResult
{
Id = id,
Uri = uri
};
});
}
public async Task<IActionResult> DoThing()
{
try
{
var cpuBoundTask = Task.Run(() =>
{
doCPUBoundStuff();
});
var ioBoundTask = DoIOBoundStuff();
// do stuff with ioBoundTask, cpuBoundTask
}
catch (System.Exception ex)
{
// Process System.Exception.AggregateException, other exceptions
}
}
Проблема здесь в том, что если что-то в одной из этих задач выдает исключение (в частности doSomethingWithIdAndUri()
), то это исключение не перехватывается блоком try …catch и приводит к сбою. Я пробовал создавать задачи продолжения с TaskContinuationOptions.OnlyOnFaulted
помощью обработки исключений, но все, что, похоже, делает, это всегда вызывает TaskCancelledException
выброс a . Как я могу перехватывать исключения, которые генерируются из задач?
Комментарии:
1. Ожидать их? [Добавление символов в соответствии с требованиями]
2. @PeterBons Да, я ожидаю cpuBoundTask, ioBoundTask, если вы это имеете в виду.
3. Я вижу задачу. Запуск, который, похоже, не ожидается, это правильно?
Ответ №1:
Для того, чтобы исключение задачи «всплывало», его нужно ожидать. Если при ожидании было выдано исключение, это исключение будет повторно выдано в текущем контексте (хотя иногда оно может быть обернуто в другой тип исключения, например, an AggregateException
.
Вам нужно либо изменить, например, var ioBoundTask = DoIOBoundStuff();
на var ioBoundTask = await DoIOBoundStuff();
, либо перехватить исключения в другом месте, ожидая выполнения задач и заключая ожидания в другой блок try-catch .
Позаботьтесь о том, чтобы ожидать каждого отдельного асинхронного метода. Избегайте использования async void
, поскольку вы не можете их ожидать. Вместо этого используйте async Task
.
ОТРЕДАКТИРОВАНО для добавления: ваше использование ContinueWith
is кажется немного странным в асинхронном контексте. Вместо того , чтобы
return await Task.WhenAll(ioBoundTask1, ioBoundTask2)
.ContinueWith((task) =>
{
var id = ioBoundTask1.Result.Id;
var uri = ioBoundTask2.Result.Uri;
doSomethingWithIdAndUri(id, uri);
return new IOBoundTaskResult
{
Id = id,
Uri = uri
};
});
Вы могли бы написать что-то вроде
var result1 = await ioBoundTask1;
var result2 = await ioBoundTask2;
var id = result1.Id;
var uri = result2.Uri;
doSomethingWithIdAndUri(id, uri);
return new IOBoundTaskResult
{
Id = id,
Uri = uri
};
Это делает более понятным, что происходит, и упрощает предотвращение таких проблем, как исключения, выбрасываемые в неправильном месте / вообще не выбрасываемые. Бывают ситуации, когда требуется более явное использование продолжений, но в целом этого следует избегать в пользу более чистого, более сжатого синтаксиса async / await.
РЕДАКТИРОВАТЬ: вот ссылка на мою версию кода, где исключение должно «всплывать» правильно.
Комментарии:
1. Да, проблема заключалась в том, что doSomethingWithIdAndUri был асинхронным void . Изменение этого и ожидание его исправили это. Спасибо.