C#async / await有/无等待(开火和遗忘)

时间:2017-09-05 10:51:57

标签: 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();
    }

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

我已经意识到使用了火,并且忘记了ASP.NET中的警告here。我之所以这样问,是因为我们正在将我们的应用转移到我们无法再使用HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken => await LongMethodAsync());的服务架构,而建议只需将其替换为Task.Run

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

2 个答案:

答案 0 :(得分:7)

  

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

这是一个糟糕的建议。您应该使用separate background process separated from your web frontend by a queue

  

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

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

答案 1 :(得分: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众所周知能够吞咽异常。事实上,你必须做的工作是不要吞下它们,然后检查它们是否在它们完成后有一个。您应该至少有一些后续任务可以记录(和可能的暴露)异常。或者你会真的后悔“火与忘记”。

希望有所帮助。