如何在不等待的情况下安全地调用C#中的异步方法
我有一个不返回数据的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()
会导致在Visual Studio中出现“因为此调用未被等待,当前方法在调用完成之前继续运行”警告。 在该警告的页面上,它指出:
只有在确定不希望等待异步调用完成并且调用的方法不会引发任何异常时 ,才应该考虑抑制警告。
我确定我不想等待电话完成; 我不需要或有时间。 但是这个调用可能会引发异常。
我偶然发现了这个问题几次,我确定这是一个常见的问题,必须有一个共同的解决方案。
如何在不等待结果的情况下安全地调用异步方法?
更新:
对于建议我只是等待结果的人,这是响应我们Web服务(ASP.NET Web API)上的Web请求的代码。 在UI上下文中等待会使UI线程保持空闲状态,但等待Web请求调用等待Task完成后再响应请求,从而无需理由地增加响应时间。
如果你想异步“异步”得到异常,你可以这样做:
MyAsyncMethod().
ContinueWith(t => Console.WriteLine(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
这将允许您处理除“主”线程以外的线程的异常。 这意味着您不必从调用MyAsyncMethod
的线程“等待”对MyAsyncMethod()
的调用; 但是,仍然允许您对异常做一些事情 - 但只有在发生异常时才会执行。
更新:
从技术上讲,你可以用await
做类似的事情:
try
{
await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
...如果你需要专门使用try
/ catch
(或using
),这会很有using
但是我发现ContinueWith
更加明确,因为你必须知道ConfigureAwait(false)
含义。
您应该首先考虑将GetStringData
为async
方法,并让它await
从MyAsyncMethod
返回的任务。
如果你确定你不需要处理来自MyAsyncMethod
异常或知道它何时完成,那么你可以这样做:
public string GetStringData()
{
var _ = MyAsyncMethod();
return "hello world";
}
顺便说一下,这不是一个“常见问题”。 想要执行某些代码并不在意它是否完成而不在乎它是否成功完成。
更新:
由于您使用的是ASP.NET并希望尽早返回,因此您可能会发现关于此主题的博文。 但是,ASP.NET不是为此设计的,因此不能保证在返回响应后您的代码将运行。 ASP.NET将尽其所能让它运行,但它不能保证它。
所以,对于简单的事情来说,这是一个很好的解决方案,就像把事件扔进日志一样,如果你在这里和那里都失去了一点,这并不重要。 这对于任何关键业务操作都不是一个好的解决方案。 在这些情况下,您必须采用更复杂的体系结构,以持久的方式保存操作(例如,Azure队列,MSMQ)和单独的后台进程(例如Azure Worker Role,Win32 Service)来处理它们。
Peter Ritchie的回答是我想要的,Stephen Cleary关于在ASP.NET中尽早返回的文章非常有用。
然而,作为一个更普遍的问题(不是特定于ASP.NET上下文),以下控制台应用程序使用Task.ContinueWith(...)
演示了Peter的答案的用法和行为。
static void Main(string[] args)
{
try
{
// output "hello world" as method returns early
Console.WriteLine(GetStringData());
}
catch
{
// Exception is NOT caught here
}
Console.ReadLine();
}
public static string GetStringData()
{
MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
return "hello world";
}
public static async Task MyAsyncMethod()
{
await Task.Run(() => { throw new Exception("thrown on background thread"); });
}
public static void OnMyAsyncMethodFailed(Task task)
{
Exception ex = task.Exception;
// Deal with exceptions here however you want
}
GetStringData()
在没有等待MyAsyncMethod()
情况下返回,并且在OnMyAsyncMethodFailed(Task task)
中处理MyAsyncMethod()
中抛出的异常,而不是在GetStringData()
周围的try
/ catch
。
上一篇: How to safely call an async method in C# without await
下一篇: Is Async await keyword equivalent to a ContinueWith lambda?