C# · 12月 26, 2021

c# – 触发并忘记异步任务方法有时不会调用

我有一个异步方法: public async Task DoSomethingAsync(){ … await … await … …. return await SaveAsync();}

在大多数时候,我通过以下方式调用此方法:

await DoSomethingAsync()

此调用按预期工作.但在某个地方,我需要将此方法称为火,并忘记:

public void OtherMethod(){ … DoSomethingAsync(); //fire and forget here}

在这种情况下,有时Task DoSomethingAsync()会运行并完成,但有时任务永远不会被调用(或在DoSomethingAsync()中调用一些等待但永远不会完成最后一次等待SaveAsync();).

我正在尝试通过以下代码确保以火灾和忘记方式调用任务:

public void OtherMethod(){ … Task.Factory.StartNew(() => { await DoSomethingAsync(); }); //fire and forget again here}

但是,这不符合预期.所以我的问题是:

>如何在没有等待的情况下调用DoSomethingAsync()将始终运行并完成? (我不关心AppDomain重启/崩溃的情况)
>如果我删除DoSomethingAsync()中的所有异步/等待代码并用.ContinueWith()替换await,那么将调用Task DoSomethingAsync()(方法声明中没有异步)的调用并确保完成(忽略大小写AppDomain重启) /崩溃),如果是的话,从通话中退出多长时间(我不认为如果在10分钟后调用任务我会很高兴)?

解决方法 等待应该工作正常,所以这里可能还有其他东西正在等待你的一个或多个方法.有几种可能性:

>你的某个方法中有某个死锁,阻止它完成并阻止等待恢复.
>由于某种原因,所有线程池线程都阻塞,阻止挂起的任务运行.
>您正在同步上下文中运行异步方法,并且正在阻止上下文,阻止它运行调度的回调.通常情况下,上下文将是UI线程,通常这很明显,因为它会锁定您的应用程序UI.

如果您可以使用VS调试器附加并观察发生的情况,请尝试暂停并查看Parallel Stacks视图.这应该有助于缩小考虑的可能性.

正如斯蒂芬指出的那样,当你称之为“即发即忘”时,也可能发生异常.如果您还没有,请确保您处理TaskScheduler.UnobservedTaskException以记录此类事件.请注意,这是从终结器调用的,因此调用它的时间是不确定的.这可能会使调试变得棘手,因为事件可能要比导致异常的实际事件晚得多.因此,我建议遵循Stephen的建议并将任务保存到等待或稍后等待其他地方.