我有一个async方法:
public async Task DoSomethingAsync(){
...
await ...
await ...
....
return await SaveAsync();
}
在大多数情况下,我调用这个方法:
await DoSomethingAsync()
此调用按预期工作。但在某处,我需要调用这个方法为fire,而忘记了:
public void OtherMethod()
{
...
DoSomethingAsync(); //fire and forget here
}
在这种情况下,有时任务DoSomethingAsync()
运行并完成,但有时任务从未被调用(或调用DoSomethingAsync()
中的某些await
,但从未完成最后一个await SaveAsync();
)。
我正在努力确保任务将被调用在火和忘记的方式通过这些代码:
public void OtherMethod()
{
...
Task.Factory.StartNew(() =>
{
await DoSomethingAsync();
}); //fire and forget again here
}
然而,这并不像预期的那样工作。所以我的问题是:
如何使呼叫
DoSomethingAsync()
没有await
将始终运行和完成?如果我删除
DoSomethingAsync()
内的所有async/await
代码,并用.ContinueWith()
替换await
,那么对Task DoSomethingAsync()
的调用(在方法声明中没有async
)将被调用并确保完成(忽略AppDomain重启/崩溃的情况),如果是,从调用多长时间(我不认为如果任务将在10分钟后调用我会很高兴)?
您可能在DoSomethingAsync
的某个地方得到一个异常,您无法观察到,因为您忽略了任务。这正是你所要求的行为,因为你在告诉代码"忘记"任务。
要观察异常,您不能"忘记"任务:
public Task OtherMethodAsync()
{
...
return DoSomethingAsync();
}
在某个点,await
(或Wait
)返回任务。
等待应该工作得很好,所以这里可能有其他事情阻碍了一个或多个等待的方法。有以下几种可能性:
- 你的某个方法发生了死锁,阻止了它的完成并阻止了等待的恢复。
- 所有的线程池线程由于某种原因阻塞,阻止了挂起的任务运行。
- 你正在同步上下文中运行async方法,并且有东西阻碍了上下文,阻止它运行已调度的回调。通常情况下,上下文是UI线程,通常这是很明显的,因为它锁定了你的应用程序UI。
如果你可以连接VS调试器并观察发生了什么,尝试暂停并查看并行堆栈视图。这应该有助于缩小考虑的可能性。
正如Stephen指出的,当你调用它时,也有可能发生一个Exception。如果你还没有,确保你已经处理了TaskScheduler。UnobservedTaskException记录任何类似的事件。注意,这是从终结器调用的,因此它被调用的时间是不确定的。这可能会使调试变得棘手,因为事件可能要比导致异常的实际事件晚得多才会触发。因此,我建议遵循Stephen的建议,将任务保存为等待或稍后在其他地方等待。
. NET通常不允许在请求中启动"即发即弃"(async void)操作。有关此行为的进一步解释和一些可能的解决方法,请参阅https://stackoverflow.com/a/22484832/59641。