异步 .NET 控制台程序中的线程控制流

Thread control flow in async .NET Console program

我在 C# 中摆弄 async/await 只是为了深入研究一些线程控制流,偶然发现了一个不寻常的行为,我真的很希望得到澄清。 即使 Task 本身在后台执行,await 之后的执行也会在调用线程上继续执行,这是有道理的。事实上,这正是发生在 WPF 中的情况。
以下代码:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    Console.WriteLine($"Start. Thread: {Thread.CurrentThread.ManagedThreadId}");
    await Task.Run(async () => await Task.Delay(1000));
    Console.WriteLine($"End. Thread: {Thread.CurrentThread.ManagedThreadId}");
}

结果:
开始。线程:1
结尾。主题:1

我意识到这是使程序流程可预测等的方法

但令我感到惊讶的是,.NET 控制台应用程序的异步 Main 方法功能显示出一些不同的行为。
相同的代码:

static async Task Main(string[] args)
{
    Console.WriteLine($"Start. Thread: {Thread.CurrentThread.ManagedThreadId}");
    await Task.Run(async () => await Task.Delay(1000));
    Console.WriteLine($"End. Thread: {Thread.CurrentThread.ManagedThreadId}");
}

导致不同的线程控制流:
开始。线程:1
结尾。主题:5

我的猜测是控制台应用程序具有不同的同步上下文概念,并且不像 WPF 那样绑定到主 "UI" 线程。但我实际上正在努力寻找一些明确的信息。

简而言之,当 SynchronizationContext.Current 未设置时,(在控制台应用程序上就是这种情况)。在 ThreadPool 上调用等待响应。

在 Winforms/WPF 上实现了一个 SynchronizationContext 来对 winforms controlToSendTo.BeginInvoke(); 或 WPF Dispatcher.BeginInvoke();.

的响应进行排队

参考:

    开发团队的
  • Await, SynchronizationContext, and Console Apps (a blog post by a member):

    But there's one common kind of application that doesn't have a SynchronizationContext: console apps. When your console application's Main method is invoked, SynchronizationContext.Current will return null. That means that if you invoke an asynchronous method in your console app, unless you do something special, your asynchronous methods will not have thread affinity: the continuations within those asynchronous methods could end up running "anywhere."

  • Parallel Computing - It's All About the SynchronizationContext (an article referenced from官方文档为SynchronizationContext class):

    By default, all threads in console applications and Windows Services only have the default SynchronizationContext.

    ...

    Figure 4 Summary of SynchronizationContext Implementations
    ...

    ╔═════════╦═══════════╦════════════╦════════════╦══════════╦══════════╗
    ║         ║ Specific  ║ Exclusive  ║ Ordered    ║ Send May ║ Post May ║
    ║         ║ Thread    ║ (Delegates ║ (Delegates ║ Invoke   ║ Invoke   ║
    ║         ║ Used to   ║ Execute    ║ Execute    ║ Delegate ║ Delegate ║
    ║         ║ Execute   ║ One at     ║ in Queue   ║ Directly ║ Directly ║
    ║         ║ Delegates ║ a Time)    ║ Order)     ║          ║          ║
    ╠═════════╬═══════════╬════════════╬════════════╬══════════╬══════════╣
    ║ ...     ║           ║            ║            ║          ║          ║
    ╠═════════╬═══════════╬════════════╬════════════╬══════════╬══════════╣
    ║ Default ║ No        ║ No         ║ No         ║ Always   ║ Never    ║
    ╚═════════╩═══════════╩════════════╩════════════╩══════════╩══════════╝