简体   繁体   English

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

[英]fire and forget an async Task method sometimes not invoke

I have an async method: 我有一个异步方法:

public async Task DoSomethingAsync(){
    ...
    await ...
    await ...
    ....
    return await SaveAsync();
}

In most time, I call this method by: 在大多数时候,我通过以下方式调用此方法:

await DoSomethingAsync()

This call works as expected. 此调用按预期工作。 But somewhere, I need to call this method as fire and forget: 但在某个地方,我需要将此方法称为火,并忘记:

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

In this case, sometimes the Task DoSomethingAsync() runs and completes, but sometimes the task never invoked (or invoke some await s within DoSomethingAsync() but never complete the last await SaveAsync(); ). 在这种情况下,有时Task DoSomethingAsync()运行并完成,但有时任务永远不会被调用(或者在DoSomethingAsync()调用一些await但永远不会完成最后一次await SaveAsync(); )。

I'm trying to make sure the task will be invoked in fire and forget manner by these code: 我正在尝试通过以下代码确保以火灾和忘记方式调用任务:

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

However, this does not work as expectation. 但是,这不符合预期。 So my questions are: 所以我的问题是:

  1. How to make the call to DoSomethingAsync() without await will always run and complete? 如何在没有await情况下调用DoSomethingAsync()将始终运行并完成? (I don't care the case AppDomain restart/crash) (我不关心AppDomain重启/崩溃的情况)

  2. If I remove all async/await code within DoSomethingAsync() and replace await by .ContinueWith() , then the call to Task DoSomethingAsync() (not have async in method declaration) will be invoked and sure to complete (ignore case AppDomain restart/crash), if yes, how long from the call (I don't think that I'll be happy if the Task will be invoked after 10 minutes)? 如果我删除所有async/await内码DoSomethingAsync()和REPLACE await通过.ContinueWith()然后调用Task DoSomethingAsync()没有async在方法声明)将被调用,并确保完成(忽略大小写的AppDomain重新启动/崩溃),如果是的话,从通话中退出多长时间(我不认为如果在10分钟后调用任务我会很高兴)?

You're probably getting an exception somewhere in DoSomethingAsync , which you cannot observe because you're ignoring the task. 您可能在DoSomethingAsync某处遇到异常,由于您忽略了该任务,因此无法观察到该异常。 This is exactly the behavior you're asking for, since you're telling the code to "forget" the task. 这正是您要求的行为,因为您告诉代码“忘记”任务。

To observe the exception, you cannot "forget" the task: 要观察异常,你不能“忘记”任务:

public Task OtherMethodAsync()
{
  ...
  return DoSomethingAsync();
}

And at some point, await (or Wait ) the returned task. 在某些时候, await (或Wait )返回的任务。 That is how you know the task will run and complete. 这就是你知道任务将运行和完成的方式。

The awaits should be working fine, so there is likely something else going on here that is holding up one or more of your methods being awaited. 等待应该工作正常,所以这里可能还有其他东西正在等待你的一个或多个方法。 There are a few possibilities: 有几种可能性:

  1. You have a deadlock somewhere in one of your methods, preventing it from completing and blocking the await from resuming. 你的某个方法中有某个死锁,阻止它完成并阻止等待恢复。
  2. All of your thread pool threads are blocking for some reason, preventing pending Tasks from running. 由于某种原因,所有线程池线程都被阻塞,导致挂起的任务无法运行。
  3. You're running the async method in a synchronization context and something is holding up the context, preventing it from running the dispatched callbacks. 您正在同步上下文中运行异步方法,并且正在阻止上下文,从而阻止它运行调度的回调。 Typically the context would be the UI thread and generally this is pretty obvious, since it locks up your application UI. 通常情况下,上下文将是UI线程,通常这很明显,因为它会锁定您的应用程序UI。

If you can attach with the VS debugger and observe what's happening, try pausing and looking at the Parallel Stacks view. 如果您可以使用VS调试器附加并观察发生的情况,请尝试暂停并查看Parallel Stacks视图。 This should help narrow down which possibilities to consider. 这应该有助于缩小考虑的可能性。


As Stephen pointed out, it's also possible that an Exception is occurring when you're calling it fire-and-forget. 正如斯蒂芬指出的那样,当你称之为“即发即忘”时,也可能发生异常。 If you're not already, make sure you handle TaskScheduler.UnobservedTaskException to log any events like this. 如果您还没有,请确保处理TaskScheduler.UnobservedTaskException以记录此类事件。 Note that this is called from the finalizer, so the time at which it gets called is non-deterministic. 请注意,这是从终结器调用的,因此调用它的时间是不确定的。 This can make debugging tricky, since the event might not fire until much later than the actual event that caused the exception. 这可能会使调试变得棘手,因为事件可能要比导致异常的实际事件晚得多。 As such, I recommend following Stephen's advice and saving the Task to await or Wait somewhere else later. 因此,我建议遵循Stephen的建议并将任务保存到等待或稍后等待其他地方。

ASP.NET generally does not allow fire-and-forget (async void) operations to be kicked off from within a request. ASP.NET通常不允许在请求中启动fire-and-forget(async void)操作。 See https://stackoverflow.com/a/22484832/59641 for a further explanation of this behavior and some potential workarounds. 有关此行为的进一步说明以及一些可能的解决方法,请参阅https://stackoverflow.com/a/22484832/59641

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM