Geo*_*ell 283 c# exception task task-parallel-library async-await
我有一个async不返回任何数据的方法:
public async Task MyAsyncMethod()
{
// do some stuff async, don't return any data
}
Run Code Online (Sandbox Code Playgroud)
我从另一个返回一些数据的方法调用它:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Run Code Online (Sandbox Code Playgroud)
在MyAsyncMethod()没有等待的情况下进行呼叫会导致" 因为此呼叫未被等待,当前方法在呼叫完成之前继续运行 "在visual studio中发出警告.在该警告的页面上,它指出:
只有当您确定不想等待异步调用完成并且被调用的方法不会引发任何异常时,才应考虑禁止警告.
我确定我不想等待电话完成; 我不需要或没有时间.但这一呼吁可能引发例外.
我偶然发现了这个问题几次,我确信这是一个必须有共同解决方案的常见问题.
如何在不等待结果的情况下安全地调用异步方法?
对于那些建议我等待结果的人来说,这是响应我们的Web服务(ASP.NET Web API)上的Web请求的代码.在UI上下文中等待保持UI线程空闲,但是在Web请求调用中等待将在响应请求之前等待任务完成,从而无缘无故地增加响应时间.
Pet*_*hie 174
如果要"异步"获取异常,可以执行以下操作:
MyAsyncMethod().
ContinueWith(t => Console.WriteLine(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
Run Code Online (Sandbox Code Playgroud)
这将允许您在"主"线程以外的线程上处理异常.这意味着您不必"等待" MyAsyncMethod()来自调用的线程的调用MyAsyncMethod; 但是,仍允许您使用异常执行某些操作 - 但仅在发生异常时才执行.
从技术上讲,你可以做类似的事情await:
try
{
await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
Run Code Online (Sandbox Code Playgroud)
...如果您需要专门使用try/ catch(或using),这将是有用的,但我发现它ContinueWith更明确,因为你必须知道什么ConfigureAwait(false)意思.
Ste*_*ary 55
您应该首先考虑制作GetStringData一个async方法并让它await返回任务MyAsyncMethod.
如果您完全确定不需要处理异常MyAsyncMethod 或知道它何时完成,那么您可以这样做:
public string GetStringData()
{
var _ = MyAsyncMethod();
return "hello world";
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这不是一个"常见问题".这是非常罕见的要执行一些代码,并不在乎它是否完成,并不在乎是否成功完成.
更新:
由于您使用的是ASP.NET并希望尽早返回,因此您可能会发现我关于该主题的博客文章很有用.但是,ASP.NET不是为此而设计的,并且无法保证在返回响应后您的代码将运行.ASP.NET将尽力让它运行,但它不能保证它.
所以,这是简单的东西喜欢折腾的事件记录到日志在它不罚款的解决方案真的,如果你失去了几这里有关系.对于任何类型的业务关键型操作而言,它都不是一个好的解决方案.在这些情况下,您必须采用更复杂的体系结构,使用持久的方法来保存操作(例如,Azure队列,MSMQ)和单独的后台进程(例如,Azure Worker Role,Win32 Service)来处理它们.
Geo*_*ell 45
Peter Ritchie的回答是我想要的,Stephen Cleary关于早期返回ASP.NET 的文章非常有用.
然而,作为一个更普遍的问题(不是特定于ASP.NET上下文),以下控制台应用程序演示了Peter使用的答案的用法和行为 Task.ContinueWith(...)
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
}
Run Code Online (Sandbox Code Playgroud)
GetStringData()返回早期,不必等待MyAsyncMethod(),并抛出的异常MyAsyncMethod()是在处理OnMyAsyncMethodFailed(Task task)和未在try/ catch左右GetStringData()
Fil*_*dji 16
我最终得到了这个解决方案:
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);
}
Run Code Online (Sandbox Code Playgroud)
Cha*_*thJ 15
这不是最佳做法,您应该尝试避免这种情况。
但是,为了解决“在 C# 中调用异步方法而不需要等待”,您可以在Task.Run. 此方法将等到MyAsyncMethod完成。
public string GetStringData()
{
Task.Run(()=> MyAsyncMethod()).Result;
return "hello world";
}
Run Code Online (Sandbox Code Playgroud)
await异步解开Result任务的包装,而仅使用Result会阻塞,直到任务完成。
如果你想将它包装在一个辅助类中:
public static class AsyncHelper
{
public static void Sync(Func<Task> func) => Task.Run(func).ConfigureAwait(false);
public static T Sync<T>(Func<Task<T>> func) => Task.Run(func).Result;
}
Run Code Online (Sandbox Code Playgroud)
并像这样打电话
public string GetStringData()
{
AsyncHelper.Sync(() => MyAsyncMethod());
return "hello world";
}
Run Code Online (Sandbox Code Playgroud)
was*_*ast 13
这被称为fire and forget,并且有一个扩展。
消耗一个任务并且不做任何事情。对于异步方法中对异步方法的即发即忘调用很有用。
安装nuget 包。
用:
MyAsyncMethod().Forget();
Run Code Online (Sandbox Code Playgroud)
我在这里参加聚会迟到了,但是我一直在使用一个很棒的库,我没有在其他答案中看到它的引用
https://github.com/brminnick/AsyncAwaitBestPractices
如果您需要“Fire And Forget”,您可以调用任务的扩展方法。
将 onException 操作传递给调用可确保您获得两全其美 - 无需等待执行并减慢用户速度,同时保留以优雅方式处理异常的能力。
在您的示例中,您将像这样使用它:
public string GetStringData()
{
MyAsyncMethod().SafeFireAndForget(onException: (exception) =>
{
//DO STUFF WITH THE EXCEPTION
});
return "hello world";
}
Run Code Online (Sandbox Code Playgroud)
它还提供了可等待的 AsyncCommands 实现 ICommand 开箱即用,这对我的 MVVM Xamarin 解决方案非常有用