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

C# 如何在异步方法中启动未等待的后台任务?,c#,.net,async-await,C#,.net,Async Await,我正在努力研究如何在异步方法的世界中执行一些非常长时间运行的后台处理 使用中的词汇表,我感兴趣的是在等待“承诺任务”之后启动“委托任务”。我希望在承诺值可用时尽快返回该值,并让委托任务在后台继续 要处理un-wait-ed委托任务中的异常,我将使用一个按照 公共异步任务ComplexProcessAsync() { ... int firstResult=await firststepsync(); //编译器不喜欢这样 Task.Run(()=>verylongSecondStepIDOnNe

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

使用中的词汇表,我感兴趣的是在等待“承诺任务”之后启动“委托任务”。我希望在承诺值可用时尽快返回该值,并让委托任务在后台继续

要处理un-
wait
-ed委托任务中的异常,我将使用一个按照

公共异步任务ComplexProcessAsync() { ... int firstResult=await firststepsync(); //编译器不喜欢这样 Task.Run(()=>verylongSecondStepIDOnNeedToWaitFor(firstResult)).LogExceptions(); 返回第一个结果; } 由于
VeryLongSecondStep…()
可能需要许多分钟,而且其结果完全可以通过副作用观察到(例如,数据库中出现的记录),因此我重申我不想等待它

但正如所指出的,编译器不喜欢这样。它警告“<代码>”,因为此调用没有等待,当前方法的执行在调用完成之前继续。请考虑将“Acess”运算符应用到调用的结果。 我可以忽略这个警告,但我感觉我没有以“正确”的方式做这件事。那么正确的方法是什么呢


谢谢

编译器正试图保护您不出错,因为当您实际需要等待某些内容时,很容易忘记
wait
,但在这种情况下,您确实不需要
wait
,因此可以忽略警告


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

由于这是在ASP.NET Web API应用程序中,我建议您使用HostingEnvironment.QueueBackgroundWorkItem。斯蒂芬·克利里也有一篇关于它的帖子

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

我建议您将
非常长的第二步idonneedtowaitfor()
移出
complexprocessaync()
方法

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

您可以通过以下方式组合这些任务:

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

我可以忽略这个警告,但我感觉我没有以“正确”的方式做这件事。那么正确的方法是什么呢

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

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

var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));
编译器足够智能,可以理解您正在使用此变量来消除“missing await”警告

另一方面,我不建议继续使用
;在异步世界中,它只是
wait
的一个更危险的版本。所以我会写
LogExceptions
这样的东西:

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

你是在ASP.NET应用程序中这样做的吗?@WilliamXifaras这有关系吗?我知道SynchronizationContext和TaskScheduler可能不同,但这在这里有意义吗?这很重要,因为我为您提供了一个适用于ASP.NET的选项。@WilliamXifaras我的应用程序是一个自托管的Web API。谢谢您的建议。如果应用程序不是ASP.NET应用程序,该怎么办?比如说WinForms之类的行业?@David Rubin我不确定,因为我不是WinForms开发人员。我必须对此进行研究。@GianPaolo这很好,除非如前所述,并附和道,“任务现在是将工作排队到线程池的首选方式。”因此,这就是我正在寻找的解决方案的主旨——如何使用我不再等待的任务系统将后台任务排队,最有效的做法是,对Stephen Cleary链接进行投票,明确指出“正确的解决方案是使用一个基本的分布式体系结构”。长时间运行的进程可以并且将被ASP.NET的主机终止,即使使用QBWI,但有时是可以接受的。谢谢您的建议,但是请考虑“<代码>第二步”的情况。()
FirstStepAsync()
的一个基本后续。也就是说,
FirstStepAsync()
SecondStep()
必须始终同时出现,因此它们被包装在
ComplexProcessAsync()中
。但现在他们没有。首先是第一步,程序等待它完成,然后开始漫长的第二步。
var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));
public static async Task LogExceptions(this Task task)
{
  try
  {
    await task.ConfigureAwait(false);
  }
  catch (Exception ex)
  {
    LogException(ex);
  }
}