启动和即发即弃异步调用的正确方式



我有一个异步调用(DoAsyncWork()(,我想以即发即弃的方式启动,即我对其结果不感兴趣,希望调用线程在异步方法完成之前继续。

做这件事的正确方法是什么?我在.NET Framework 4.6和.NET Core 2中都需要这一点,以防出现差异。

public async Task<MyResult> DoWorkAsync(){...}
public void StarterA(){
Task.Run(() => DoWorkAsync());
}
public void StarterB(){
Task.Run(async () => await DoWorkAsync());
}

是这两种中的一种还是不同的/更好的?

//编辑:理想情况下没有任何额外的库。

这样做的正确方法是什么?

首先,你需要决定你是否真的想要即发即弃。根据我的经验,大约90%的人要求这样做,实际上他们不想得到即发即弃;他们想要一个后台处理服务。

具体来说,即发即弃是指:

  1. 您不在乎操作何时完成
  2. 您不在乎在执行操作时是否有任何异常
  3. 您根本不在乎操作是否完成

因此,即发即弃的实际使用案例小得惊人。更新服务器端缓存之类的操作是可以的。发送电子邮件、生成文档或任何与业务相关的操作都是不可以的,因为您(1(希望完成该操作,(2(如果该操作出现错误,则会收到通知。

绝大多数时候,人们根本不想即发即弃;他们想要一个后台处理服务。构建其中一个的正确方法是添加一个可靠的队列(例如,Azure队列/Aamazon SQS,甚至数据库(,并让一个独立的后台进程(例如,AzureFunction/Aamazon-Lambda/.NET Core BackgroundService/W32服务(处理该队列。这基本上就是Hangfire所提供的(使用数据库作为队列,并在ASP.NET进程中的proc中运行后台进程(。

是这两者中的一个还是不同/更好的东西?

在一般情况下,删除asyncawait时会有一些小的行为差异。这不是你想"默认"做的事情。

但是,在这种特定的情况下——asynclambda只调用一个方法——删除asyncawait是可以的。

这取决于你所说的正确:(

例如:您对"fire-and-forget"调用中抛出的异常感兴趣吗?如果不是的话,那么这就有点好了。尽管你可能需要考虑的是任务所处的环境。

例如,如果这是一个asp.net应用程序,并且您在由于调用.aspx或.svc而实例化的线程的生存期内执行此操作。Task将成为该(前台(线程的后台线程。前台线程可能会在"激发并忘记"任务完成之前被应用程序池清理掉。

所以也要考虑一下你的任务在哪个线程中。

我认为这篇文章为您提供了一些有用的信息:https://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx

还要注意,如果您不在任务中返回值,则任务将不会返回异常信息。其来源是微软考试70-483的参考书网上可能有一个免费版本;Phttps://www.amazon.com/Exam-Ref-70-483-Programming-C/dp/0735676828

也许有用的是,如果你有一个异步方法被非异步调用,并且你想知道它的结果。您可以使用.GetAwaiter((.GetResult((.

另外,我认为重要的是要注意异步和多线程之间的区别。

只有当操作使用计算机的其他部分而不是CPU时,异步才有用。比如网络或I/O操作。使用async,然后告诉系统继续在其他地方使用CPU电源,而不是"阻塞"CPU中的线程等待响应。

多线程是在CPU中的不同线程上分配操作(例如,创建一个任务,该任务创建前台线程的后台线程……前台线程是组成应用程序的线程,它们是主要的,后台线程与前台线程链接。如果关闭链接的前台线程,后台线程也会关闭(这允许CPU同时处理不同的任务。

如果是4线程CPU,那么将这两者结合起来可以确保CPU不会在4个线程上被阻塞。但是在等待等待I/O操作的异步任务时可以打开更多。

我希望这能给你所需的信息,无论你在做什么:(

最新更新