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

Proper way to start and async fire-and-forget call?

我有一个异步调用 (DoAsyncWork()),我想以一劳永逸的方式开始,即我对其结果不感兴趣,希望调用线程继续甚至在异步方法完成之前。

正确的做法是什么?我在 .NET Framework 4.6 和 .NETCore 2 中都需要这个,以防出现差异。

public async Task<MyResult> DoWorkAsync(){...}

public void StarterA(){
    Task.Run(() => DoWorkAsync());
}

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

是这两个之一还是什么different/better?

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

这取决于你的意思:)

例如:您对 "fire and forget" 调用中抛出的异常感兴趣吗?如果没有,那还好。尽管您可能需要考虑的是任务所处的环境。

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

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

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

另请注意,如果您没有 return 任务中的值,任务将不会 return 异常信息。其来源是微软考试 70-483 的参考书 网上某个地方可能有一个免费版本;P https://www.amazon.com/Exam-Ref-70-483-Programming-C/dp/0735676828

如果您有一个由非异步调用的异步方法并且您希望知道其结果,知道这一点可能很有用。您可以使用 .GetAwaiter().GetResult().

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

异步仅在有操作使用计算机的其他部分而不是 CPU 时才有用。所以诸如网络或 I/O 操作之类的东西。然后使用异步告诉系统继续并在其他地方使用 CPU 电源而不是 CPU 中的 "blocking" 那个线程只是等待响应。

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

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

我希望这能为您提供所需的信息,无论您在做什么:)

What is the proper way to do this?

首先,你需要决定你是否真的想要fire-and-forget。根据我的经验,大约 90% 的提出此要求的人实际上 想要即发即弃;他们想要后台处理服务。

具体来说,即发即弃意味着:

  1. 您不关心操作何时完成。
  2. 你不关心执行动作时有没有异常。
  3. 您根本不关心操作是否完成

因此,即发即弃的现实世界用例非常少。像更新服务器端缓存这样的操作就可以了。发送电子邮件、生成文档或任何业务相关 OK,因为您 (1) 希望操作完成,并且 (2 ) 在操作出错时收到通知。

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

Is it one of those two or something different/better?

一般情况下,there's a number of small behavior differences when eliding async and await。这不是你想做的事 "by default".

但是,在这种特定情况下 - async lambda 仅调用单个方法 - 省略 asyncawait 没问题。