https://www.cnblogs.com/sjyforg/p/3949029.html
看到大佬的文章,小弟来测试下
1.winform应用程序
private void button94_Click(object sender, EventArgs e)
{
TestAsync();
}
private async void TestAsync()
{
Console.WriteLine($"主线程调用异步之前ID{Thread.CurrentThread.ManagedThreadId}执行");
await Test();
Console.WriteLine($"异步后的线程ID{Thread.CurrentThread.ManagedThreadId}同步上下文:{SynchronizationContext.Current?.ToString()}开始执行");
}
private static Logger logger = LogManager.GetCurrentClassLogger();
private async Task Test()
{
await GetHoliday("2021-01");
}
private async Task GetHoliday(string date)
{
WebClient client = new WebClient();
client.Encoding = Encoding.UTF8;
var url = $"https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query={date}&resource_id=6018";
var jsondata =await client.DownloadStringTaskAsync(url);
}
输出
主线程调用异步之前ID1执行
异步后的线程ID1同步上下文:System.Windows.Forms.WindowsFormsSynchronizationContext开始执行
可以看出:调用之前ID为1,调用以后线程ID为1,同步上下文为 System.Windows.Forms.WindowsFormsSynchronizationContext.
经过多次测试:调用前后线程ID一样。
2.控制台程序
static void Main(string[] args)
{
TestAsync();
Console.ReadKey();
}
private static async void TestAsync()
{
Console.WriteLine($"调用异步之前ID{Thread.CurrentThread.ManagedThreadId}执行");
await Test();
Console.WriteLine($"异步后的线程ID{Thread.CurrentThread.ManagedThreadId}同步上下文:{SynchronizationContext.Current?.ToString()}开始执行");
}
private static Logger logger = LogManager.GetCurrentClassLogger();
private static async Task Test()
{
try
{
await GetHoliday("2021-01");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static async Task GetHoliday(string date)
{
WebClient client = new WebClient();
client.Encoding = Encoding.UTF8;
var url = $"https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query={date}&resource_id=6018";
var jsondata = await client.DownloadStringTaskAsync(url);
}
输出日志:
可以看出:调用之前ID为1,调用以后线程ID为11,同步上下文为 null.
经过多次测试:调用前后线程ID不一样。
3.Asp.net WebApi
public async Task<string> getPATData([FromUri]PATQuery patQuery)
{
try
{
Logs.Info($"主线程调用异步之前ID{Thread.CurrentThread.ManagedThreadId}执行");
var data = new
{
list = await patService.GetPAT(patQuery),
total = patQuery.records,
};
Logs.Info($"异步后的线程ID{Thread.CurrentThread.ManagedThreadId}同步上下文:{SynchronizationContext.Current?.ToString()}开始执行");
return data.ToJson();
}
catch (Exception e)
{
Logs.LogWriter(e.GetOriginalException());
}
return string.Empty;
}
输出日志:
2022-03-05 14:07:23.2669 INFO 主线程调用异步之前ID12执行
2022-03-05 14:07:24.0974 INFO 异步后的线程ID11同步上下文:System.Web.LegacyAspNetSynchronizationContext开始执行
2022-03-05 14:07:24.6551 INFO 主线程调用异步之前ID9执行
2022-03-05 14:07:25.4546 INFO 异步后的线程ID9同步上下文:System.Web.LegacyAspNetSynchronizationContext开始执行
可以看出:
第一次调用之前ID为12,调用以后线程ID为11,同步上下文为 System.Web.LegacyAspNetSynchronizationContext
第二次调用之前ID为9,调用以后线程ID为9,同步上下文为 System.Web.LegacyAspNetSynchronizationContext
经过多次测试:调用前后线程ID可能一样可能不一样。
结论:
同步上下文(SynchronizationContext)
“同步上下文是一种可以将工作单元排队到上下文(主要是不同的线程)的方法。 它的作用通俗来讲就是实现线程之间通讯的。
在99.9%的使用场景中,SynchronizationContext仅仅被当作一个提供虚(virtual)Post方法的类,
该方法可以接收一个委托,然后异步执行它。虽
然SynchronizationContext还有许多其他的虚成员,
但是很少使用它们,而且和我们今天的内容无关,就不说了。
Post方法的基础实现就仅仅是调用一下ThreadPool.QueueUserWorkItem,
将接收的委托加入线程池队列去异步执行。
另外,派生类可以选择重写(override)Post方法,让委托在更加合适的位置和时间去执行。
例如,WinForm有一个派生自SynchronizationContext的类,重写了Post方法,
内部执行Control.BeginInvoke,
这样,调用该Post方法就会在该控件的UI线程上执行接收的委托。
WinForm依赖Win32的消息处理机制,并在UI线程上运行“消息循环”,
该线程就是简单的等待新消息到达,然后去处理。
这些消息可能是鼠标移动和点击、键盘输入、系统事件、可供调用的委托等。
所以,只需要将委托传递给SynchronizationContext实例的Post方法,就可以在控件的UI线程中执行。
和WinForm一样,WPF也有一个派生自SynchronizationContext的类,重写了Post方法,
通过Dispatcher.BeginInvoke将接收的委托封送到UI线程。
与WinForm通过控件管理不同的是,WPF是由Dispatcher管理的。
ASPNET同步上下文
AspNetSynchronizationContext
命名空间:System.Web.dll:System.Web [internal class]
实现:
ASP.NET 同步上下文在执行页面代码时安装在线程池线程上。当委托排队到捕获的AspNetSynchronizationContext时,它将还原原始页面的标识和区域性,然后直接执行委托。即使通过调用Post将其“异步”排队,也可以直接调用该委托。
从概念上讲,AspNetSynchronizationContext的上下文很复杂。在异步页的生存期内,上下文仅从ASP.NET线程池中的一个线程开始。启动异步请求后,上下文不包含任何线程。随着异步请求的完成,执行其完成例程的线程池线程将进入上下文。这些线程可能是发起请求的线程,但更有可能是操作完成时空闲的任何线程。