异步/等待有/无等待(即发即弃)

Posted

技术标签:

【中文标题】异步/等待有/无等待(即发即弃)【英文标题】:Async/await with/without awaiting (fire and forget) 【发布时间】:2017-09-05 10:51:56 【问题描述】:

我有以下代码:

static async Task Callee()

    await Task.Delay(1000);


static async Task Caller()

    Callee(); // #1 fire and forget
    await Callee(); // #2 >1s
    Task.Run(() => Callee()); // #3 fire and forget
    await Task.Run(() => Callee()); // #4 >1s
    Task.Run(async () => await Callee()); // #5 fire and forget
    await Task.Run(async () => await Callee()); // #6 >1s


static void Main(string[] args)

    var stopWatch = new Stopwatch();
    stopWatch.Start();
    Caller().Wait();
    stopWatch.Stop();
    Console.WriteLine($"Elapsed: stopWatch.ElapsedMilliseconds");
    Console.ReadKey();

#1 以最简单的方式触发并忘记。 #2 只是等待。有趣的东西从#3 开始。调用背后的深层逻辑是什么?

我知道在 ASP.NET 中使用 fire'n'forget 警告,正如 here 所指出的那样。我问这个,因为我们正在将我们的应用程序移动到服务结构,我们不再可以使用HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync());,建议只是用Task.Run 替换它。

我看到Task.Run 运行了一个新线程,那么#3 和#5 有什么区别?

【问题讨论】:

请定义“有趣的东西”。 Task.Run 使用单独的线程。 Fire-and-forget with async vs "old async delegate"的可能重复 Tl;如果你想一劳永逸,请不要使用async/await 不,它不是@Christopher,它是异步编程,而不是多线程编程。 Aync 重用空闲的线程,多线程 (Task) 创建了新线程来独立于主线程进行处理。 换句话说,asyncawait 是不同的,你在单独的线程中运行新的异步方法等待某事,它不会冻结你的主线程因为它本身是异步的 【参考方案1】:

我问这个,因为我们正在将我们的应用程序移动到服务结构,我们不再可以使用 HostingEnvironment.QueueBackgroundWorkItem(async cancelToken => await LongMethodAsync());建议是简单地将其替换为 Task.Run。

这是个坏建议。你应该使用separate background process separated from your web frontend by a queue。

调用背后的深层逻辑是什么?

    在当前线程上启动异步方法。忽略所有结果(包括例外)。 在当前线程上启动异步方法。异步等待它完成。这是调用异步代码的标准方式。 在线程池线程上启动异步方法。忽略所有结果(包括例外)。 在线程池线程上启动异步方法。异步等待它完成。 与#3 完全相同。 与#4 完全相同。

【讨论】:

那么#3 是“正确”的方法吗?试图根据我的问题在这里确定:***.com/questions/67243044/… @Terry:我完全不推荐“一劳永逸”。如果您使用的是 ASP.NET pre-Core,并且偶尔会丢失“即发即弃”的工作而没有任何日志或工作丢失通知,那么我会说 #3 或 #5 就可以了。 【参考方案2】:

“26. “一劳永逸”很好,前提是你永远不会真正忘记。” Maxim 26.

如果您执行任何类型的“一劳永逸”的场景,您将面临吞下异常的巨大风险。吞下任何异常——尤其是致命的异常——是异常处理的致命罪过。你最终得到的只是内存中的一个程序,它会产生更难以理解和可重现的异常。所以永远不要开始。这里有两篇关于母校的好文章:

http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx http://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET

众所周知,Full on Threads 能够吞下异常。事实上,你必须努力不吞下它们,然后在它们完成后检查它们是否有一个。您应该至少有一些后续任务来记录(以及可能的暴露)异常。不然你真的会后悔那次“一劳永逸”。

希望对您有所帮助。

【讨论】:

这句话太棒了。 对于 ASP 添加:fire-and-forget-on-asp-net

以上是关于异步/等待有/无等待(即发即弃)的主要内容,如果未能解决你的问题,请参考以下文章

Mdb 与 EJB 3.1 异步方法

如何实现从一个进程到另一个进程的即发即弃消息传递?

MEF插件架构上的“即发即弃”方法

演员模型不是一种反模式吗,因为即发即弃的风格迫使演员记住一个状态?

event_loop 在 Django>=3.1 异步视图中存在多长时间

针对 C/C++ 远程消息队列的建议