控制台中的异步无效方法不想触发

Async void method in Console doesn't want to fire

为什么我在运行下面的程序时在控制台中看不到任何东西?

Screenshot for proof.

当我取消注释这两行时,我看到了两条完成消息(为什么不仅 "Foo Done!"?)

class Program
{
    static void Main(string[] args)
    {
        //var foo = new Foo();
        var bar = new Bar();
    }

    private class Foo
    {
        public Foo()
        {
            DoWork();
        }

        private void DoWork()
        {
            Console.WriteLine("Foo Done!");
        }
    }

    private class Bar
    {
        public Bar()
        {
            DoWorkAsync();
        }

        private async void DoWorkAsync()
        {
            await Task.Run(() => Console.WriteLine("Bar Done!"));
        }
    }
}

就这么简单:

只需添加

Console.ReadLine(); // waits for a key to be pressed on form

调用后

var bar = new Bar();

等待用户操作。

正如@Servy 所指出的那样,这是一种不确定的行为 - 我无法在我的机器上得到它未打印,你无法得到它打印在你身上。

为什么它是非确定性的?因为:

  1. 任务创建为 background threads
  2. 控制台有 no synchronization context

这意味着您 运行 的任务及其延续(通过异步等待程序注册)都将 运行 在后台线程上,因此如果主线程(整个应用程序)在它之前结束能够打印,你什么也看不到。

为什么在第二种情况下为您打印?再次 - 因为它是一种取决于任务调度和操作系统状态的非确定性行为。

But why I see both messages when I uncomment both lines? Why not only "Foo done!" ?

您的代码在 Main 的终止和线程池线程上的委托执行之间存在竞争条件,因此您的结果将是不确定的。如果 Task 执行委托的速度足够快,您会看到两者。不能保证一个会比另一个先完成。

运行 你的代码足够多次,你最终可能只会抓到 "Foo Done"。

当您都取消注释时,您可能会很幸运地看到 "Bar Done!"

因为你是 运行 Bar 异步主线程可能在它有机会写入控制台之前完成。添加 Foo 可能会减慢主线程的速度,足以让写入工作正常进行。

尝试等待 DoWorkAsync

private class Bar { 
    public Bar() { 
        DoWorkAsync().Wait(); 
    } 

    private async Task DoWorkAsync()
   { 
       return await Task.Run(() => Console.WriteLine("Bar Done!")); 
   } 
}