在异步任务方法中使用“fire and forget”有时候无法调用。

7

我有一个异步方法:

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

大多数情况下,我会通过以下方式调用此方法:

await DoSomethingAsync()

这个调用按预期工作。但是在某些地方,我需要将此方法作为“fire and forget”调用:
public void OtherMethod()
{
    ...
    DoSomethingAsync(); //fire and forget here
}

在这种情况下,有时任务DoSomethingAsync()会运行并完成,但有时任务从未被调用(或者在DoSomethingAsync()中调用了一些await但从未完成最后一个await SaveAsync();)。
我试图通过以下代码确保任务以“fire and forget”的方式被调用:
public void OtherMethod()
{
    ...
    Task.Factory.StartNew(() =>
    {
        await DoSomethingAsync();
    }); //fire and forget again here
}

然而,这并没有像期望的那样运行。所以我的问题是:
  1. 如何在没有使用await的情况下调用DoSomethingAsync(),使其始终运行且完成?(我不关心AppDomain重新启动/崩溃的情况)

  2. 如果我删除DoSomethingAsync()中所有的async/await代码,并使用.ContinueWith()替换await,则调用Task DoSomethingAsync()(方法声明中没有async)将被调用并确保完成(忽略AppDomain重新启动/崩溃的情况)。如果可以,请问从调用开始需要多长时间(如果任务在10分钟后才被调用,我想我不会感到满意)?

3个回答

5
您可能在DoSomethingAsync中某处遇到了异常,但由于您忽略了这个任务,因此无法观察到异常。这正是您所要求的行为,因为您告诉代码要“忘记”这个任务。
要观察异常,您不能“忘记”这个任务:
public Task OtherMethodAsync()
{
  ...
  return DoSomethingAsync();
}

在某个时间点,使用await(或 Wait)等待返回的任务。这是您知道任务将运行并完成的方式。


啊,我错过了异常情况;我假设OP正在挂钩TaskScheduler.UnobservedTaskException,但这不一定是一个有效的假设。 - Dan Bryant
嗯,实际上 DoSomethingAsync() 的主体被 try catch 包装了起来,如果出现异常,则会记录异常。但是我没有看到任何异常被生成。 - langtu
@langtu:如果是这样,那么像DanBryant所建议的那样,可能会出现死锁的情况。 - Stephen Cleary

4
等待应该正常工作,因此可能有其他原因导致您的一个或多个方法被等待。 有几种可能性:
  1. 您的某个方法中存在死锁,阻止其完成并阻止等待继续。
  2. 由于某种原因,所有线程池线程都正在阻塞,从而防止挂起的任务运行。
  3. 您正在同步上下文中运行异步方法,并且某些内容阻止上下文运行分派的回调。 通常上下文将是UI线程,通常这很明显,因为它会锁定应用程序UI。

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


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

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接