C#async/await有/无等待(火灾和忘记)

mcs*_*odo 1 c# asynchronous async-await

我有以下代码:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

#1以最简单的方式点火和忘记.#2只是等待.有趣的东西从#3开始.这些电话背后的深度逻辑是什么?

我知道在ASP.NET中使用fire'n'forget作为警告的指向这里.我问这个问题,因为我们正在将我们的应用程序转移到我们不再使用的服务结构上,HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync());建议只需将其替换为Task.Run.

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

Ste*_*ary 8

我问这个,因为我们正在将我们的应用程序移动到服务结构,我们不再使用HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync()); 并且建议只需用Task.Run替换它.

这是不好的建议.您应该使用与Web前端分开单独后台进程.

这些电话背后的深度逻辑是什么?

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

  • @Terry:我根本不推荐“即发即忘”。如果您使用的是 ASP.NET pre-Core,并且可以接受偶尔丢失“即发即忘”的工作,并且没有任何日志或工作已丢失的通知,那么我想说#3 或#5 就可以了。 (2认同)