异步 .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 ║
╚═════════╩═══════════╩════════════╩════════════╩══════════╩══════════╝
我在 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'sMain
method is invoked,SynchronizationContext.Current
will returnnull
. 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 ║ ╚═════════╩═══════════╩════════════╩════════════╩══════════╩══════════╝