Как безопасно вызывать метод async на С# без ожидания
У меня есть метод async
, который не возвращает данных:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
Я вызываю это из другого метода, который возвращает некоторые данные:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Вызов MyAsyncMethod()
, не ожидая этого, вызывает " Поскольку этот вызов не ожидается, текущий метод продолжает выполняться до завершения вызова" предупреждение в визуальной студии. На странице этого предупреждения указано:
Вам следует рассмотреть возможность подавления предупреждения только в том случае, если вы уверены, что не хотите ждать завершения асинхронного вызова и что вызываемый метод не будет создавать никаких исключений.
Я уверен, что не хочу дождаться завершения звонка; Мне не нужно или есть время. Но вызов может вызвать исключения.
Я несколько раз наткнулся на эту проблему, и я уверен, что это общая проблема, которая должна иметь общее решение.
Как безопасно вызывать метод async, не ожидая результата?
Update:
Для людей, предлагающих, что я просто жду результата, это код, который отвечает на веб-запрос в нашем веб-сервисе (ASP.NET Web API). В ожидании контекста пользовательского интерфейса поддерживает поток пользовательского интерфейса, но ожидание вызова веб-запроса будет ждать завершения задачи перед ответом на запрос, тем самым увеличивая время ответа без причины.
Ответы
Ответ 1
Если вы хотите получить исключение "асинхронно", вы можете сделать:
MyAsyncMethod().
ContinueWith(t => Console.WriteLine(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
Это позволит вам справиться с исключением из потока, отличного от "основного" потока. Это означает, что вам не нужно "ждать" вызова MyAsyncMethod()
из потока, который вызывает MyAsyncMethod
; но все же позволяет вам делать что-то с исключением - но только в случае возникновения исключения.
Update:
технически вы можете сделать что-то подобное с await
:
try
{
await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
... что было бы полезно, если бы вам нужно было специально использовать try
/catch
(или using
), но я нахожу ContinueWith
немного более явным, потому что вы должны знать, что ConfigureAwait(false)
означает.
Ответ 2
Сначала вы должны рассмотреть способ GetStringData
a async
и иметь await
задачу, возвращенную из MyAsyncMethod
.
Если вы абсолютно уверены, что вам не нужно обрабатывать исключения из MyAsyncMethod
или знать, когда они завершатся, вы можете сделать это:
public string GetStringData()
{
var _ = MyAsyncMethod();
return "hello world";
}
Кстати, это не "общая проблема". Очень редко хочется выполнить какой-то код и не заботиться о том, завершает ли он и не заботится, успешно ли он завершен.
Update:
Поскольку вы находитесь на ASP.NET и хотите вернуться раньше, вы можете найти мое сообщение
Ответ 3
Ответ Peter Ritchie был тем, что я хотел, и
GetStringData()
возвращает раньше, не ожидая MyAsyncMethod()
, а исключения, сброшенные в MyAsyncMethod()
, рассматриваются в OnMyAsyncMethodFailed(Task task)
и не в try
/catch
вокруг GetStringData()
Ответ 4
Я получаю это решение:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
public string GetStringData()
{
// Run async, no warning, exception are catched
RunAsync(MyAsyncMethod());
return "hello world";
}
private void RunAsync(Task task)
{
task.ContinueWith(t =>
{
ILog log = ServiceLocator.Current.GetInstance<ILog>();
log.Error("Unexpected Error", t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}
Ответ 5
Это называется огнем и забудь, и для этого есть расширение.
Потребляет задачу и ничего с ней не делает. Полезно для вызова "забывай и забывай" асинхронных методов внутри асинхронных методов.
Установите пакет nuget.
Использование:
MyAsyncMethod().Forget();
Ответ 6
Я думаю, возникает вопрос: зачем вам это нужно? Причина async
в С# 5.0 заключается в том, что вы можете ждать результата. Этот метод на самом деле не асинхронный, а просто вызываемый одновременно, чтобы не мешать слишком большому потоку текущего потока.
Возможно, лучше начать поток и оставить его для завершения самостоятельно.
Ответ 7
В технологиях с циклами сообщений (не уверен, является ли ASP одним из них), вы можете заблокировать цикл и обрабатывать сообщения до завершения задачи и использовать ContinueWith, чтобы разблокировать код:
public void WaitForTask(Task task)
{
DispatcherFrame frame = new DispatcherFrame();
task.ContinueWith(t => frame.Continue = false));
Dispatcher.PushFrame(frame);
}
Этот подход аналогичен блокировке на ShowDialog и по-прежнему поддерживает отзывчивость пользовательского интерфейса.