Re: Неожиданный вызов асинхронного метода против Task.Run ()
Так как в Post
имеется лишь небольшая часть работы с процессором, то есть создание полезной нагрузки json,нет никакой выгоды от другого Task.Run
- накладные расходы по планированию новой задачи в пуле потоков перевесят любую выгоду IMO.то есть
Post(action, message, LogLevel.Info);*/ // Or should I just use it like this?
- лучший из двух подходов.Вы, вероятно, захотите подавить предупреждение компилятора, связанное с незапланированными Задачами, и оставить комментарий для следующего разработчика, который встретит код.
Но в соответствии с окончательным ответом Стивена Клири, запустить и забыть в ASP.Net почти никогда не бывает хорошей идеей .Предпочтительнее было бы перенести работу, например, через очередь, в службу Windows, веб-задание Azure и др.
Существуют дополнительные опасности - если выдает незапланированное задание, вам нужно наблюдать заисключение .
Кроме того, обратите внимание, что любая работа, выполненная после Post
(например, если вы работаете с response
), что это все еще является Задачей продолжения, которую необходимо запланировать в Пуле потоков - есливы запускаете большие объемы вашего Post
метода, и при завершении они будут вызывать много конфликтов.
Re: Кроме того, если я не использую await с Task.Run (), я заблокирую поток?
await
не требует потока .await
является синтаксическим сахаром, чтобы попросить компилятор переписать ваш код асинхронно.Task.Run()
запланирует вторую задачу в ThreadPool, которая выполнит лишь небольшое количество работы, прежде чем достигнет метода PostAsync
, поэтому рекомендация не использовать его.
КоличествоИспользование / блокировка потока вызывающего абонента при незапланированном вызове от Info
до Post
зависит от того, какая работа выполняется перед возвратом Task
.В вашем случае работа по сериализации Json будет выполняться в потоке вызывающего (я пометил # 1), однако время выполнения должно быть незначительным по сравнению с продолжительностью HTTP-вызова.Поэтому, хотя метод Info
и не ожидается, любой код после HTTP-вызова все равно необходимо запланировать после завершения вызова Http и запланировать в любом доступном потоке (# 2).
public void Info(string action, string message)
{
#pragma warning disable 4014 // Deliberate fire and forget
Post(action, message, LogLevel.Info); // Unawaited Task, thread #1
#pragma warning restore 4014
}
private async Task Post(string action, string message, LogLevel logLevel)
{
var jsonData = JsonConvert.SerializeObject(log); // #1
var content = new StringContent(jsonData, Encoding.UTF8, "application/json"); // #1
var response = await httpClient.PostAsync(...), content);
// Work here will be scheduled on any available Thread, after PostAsync completes #2
}
Re: Обработка исключений
try..catch
блоки работают с асинхронным кодом - await
будет проверять наличие сбоя Task
и выдавать исключение:
public async Task Post()
{
try
{
// ... other serialization code here ...
await HttpPostAsync();
}
catch (Exception ex)
{
// Do you have a logger of last resort?
Trace.WriteLine(ex.Message);
}
}
Хотя вышеперечисленное будет соответствовать критериям для наблюдения исключения, все же будет хорошей идеей зарегистрировать обработчик UnobservedTaskException
на глобальном уровне.
Это поможет вам обнаружить и определить, где вы не смогли наблюдать исключение:
TaskScheduler.UnobservedTaskException += (sender, eventArgs) =>
{
eventArgs.SetObserved();
((AggregateException)eventArgs.Exception).Handle(ex =>
{
// Arriving here is BAD - means we've forgotten an exception handler around await
// Or haven't checked for `.IsFaulted` on `.ContinueWith`
Trace.WriteLine($"Unobserved Exception {ex.Message}");
return true;
});
};
Обратите внимание, что описанный выше обработчик запускается только тогда, когда задача собрана GC, котораяможет быть через некоторое время после возникновения исключения.