如何在异步方法中启动期待已久的后台任务?

时间:2015-11-20 19:25:28

标签: c# .net async-await

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

使用Stephen Cleary's blog中的词汇表,我有兴趣在 await之后启动“委托任务” - 承诺“承诺任务”。我希望尽快返回承诺的值,并让委托任务在后台继续。

要处理非await - ed委托任务中的异常,我将使用在the one described here.

之后建模的异常处理延续
public async Task<int> ComplexProcessAsync()
{
  ...
  int firstResult = await FirstStepAsync();

  // THE COMPILER DOESN'T LIKE THIS
  Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult)).LogExceptions();

  return firstResult;
}

由于VeryLongSecondStep...()可以多分钟,并且其结果仅通过副作用(例如,出现在数据库中的记录)可观察到,我 reeeaaalllyy don我想要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."

我可以忽略警告,但我感觉我不是以“正确”的方式做这件事。那么正确的方式呢?

谢谢!

4 个答案:

答案 0 :(得分:7)

  

我可以忽略这个警告,但我感觉我并没有在&#34;纠正&#34;办法。那么正确的方法是什么?

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

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

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

编译器足够聪明,可以理解你是否正在使用这个变量来静音#34;缺少await&#34;警告。

在旁注中,我根本不推荐ContinueWith;在异步世界中,它只是await的一个更危险的版本。所以我写LogExceptions这样的东西:

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

答案 1 :(得分:3)

由于这是在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).aspx http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

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

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

您可以通过Task.WhenAny()

组合这些任务
var result = await ComplexProcessAsync();
await Task.WhenAny(VeryLongSecondStepIDontNeedToWaitFor(firstResult), YourSecondMethod(firstResult))

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