如何在异步方法中启动未等待的后台任务

本文关键字:等待 后台任务 启动 异步方法 | 更新日期: 2023-09-27 18:23:51

我正在努力解决如何在异步方法的世界中执行一些非常的长期后台处理。

使用Stephen Cleary博客中的词汇,我有兴趣在await之后开始"委托任务"——完成"承诺任务"。我想在承诺值可用时尽快返回,并让委派任务在后台继续。

为了处理un-await-ed委托任务中的异常,我将使用以此处描述的异常处理延续为模型的异常处理继续。

public async Task<int> ComplexProcessAsync()
{
  ...
  int firstResult = await FirstStepAsync();
  // THE COMPILER DOESN'T LIKE THIS
  Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult)).LogExceptions();
  return firstResult;
}

由于VeryLongSecondStep...()可能需要许多分钟,并且其结果完全可以通过副作用(例如数据库中出现的记录)观察到,因此我当然不想await

但正如所指出的,编译器不喜欢这样。它警告"Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."

我可以忽略这个警告,但我觉得我做这件事的方式不"正确"。那么是什么才是正确的方式呢?

谢谢!

如何在异步方法中启动未等待的后台任务

我可以忽略这个警告,但我觉得我做这件事的方式不"正确"。那么正确的方法是什么呢?

编译器警告您没有在等待任务。根据我的经验,99%以上的编译器是正确的,代码是错误的,因为它缺少await。因此,这是罕见的情况之一,你知道自己在做什么,并想危险地生活。:)

在这种情况下,您可以显式地将任务分配给一个未使用的变量:

var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));

编译器足够聪明,可以理解您正在使用此变量来消除"missingawait"警告。

顺便说一句,我根本不推荐ContinueWith;在异步世界中,它只是await的一个更危险的版本。所以我会这样写LogExceptions

public static async Task LogExceptions(this Task task)
{
  try
  {
    await task.ConfigureAwait(false);
  }
  catch (Exception ex)
  {
    LogException(ex);
  }
}

由于这是在ASP.NET Web API应用程序中,我建议您使用HostingEnvironment.QueueBackgroundWorkItem。Stephen Cleary也有关于它的文章。

        HostingEnvironment.QueueBackgroundWorkItem(ct =>
        {
            try
            {
                // your code
            }
            catch (Exception ex)
            {
                // handle & log exception
            }
        });

https://msdn.microsoft.com/en-us/library/dn636893(v=vs.110).aspxhttp://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html

编译器试图保护您免受错误的影响,因为当您真正需要等待某件事时,很容易忘记await,但在这种情况下,您确实不需要await,因此可以忽略警告。

然而,在这种情况下,我认为最好在线程池中的某个线程上运行任务,因为如果像现在这样在主线程上运行它,可能会导致应用程序对新的传入请求/事件没有响应。

我建议您将VeryLongSecondStepIDontNeedToWaitFor()ComplexProcessAsync()方法中移出。

ComplexProcessAsync()本身返回一个值,该值至少被两个任务使用(其中一个是Task.Run(()=>VeryLongSecondStepIDontNeedToWaitFor(firstResult))。

您可以通过Task.WhenAny():组合这些任务

var result = await ComplexProcessAsync();
await Task.WhenAny(VeryLongSecondStepIDontNeedToWaitFor(firstResult), YourSecondMethod(firstResult))

其中YourSecondMethod(firstResult)是使用ComplexProcessAsync()结果的方法。