async/await 是我们在 ASP.NET 应用程序中,写异步代码最常用的两个关键字,使用它俩,我们不需要考虑太多背后的东西,比如异步的原理等等,如果你的 ASP.NET 应用程序是异步到底的,包含数据库访问异步、网络访问异步、服务调用异步等等,那么恭喜你,你的应用程序是没问题的,但有一种情况是,你的应用程序代码比较老,是同步的,但现在你需要调用异步代码,这该怎么办呢?有人可能会说,很简单啊,不是有个 .Result 吗?但事实真的就这么简单吗?我们来探究下。
首先,放出几篇经典文章:
上面文章的内容,我们后面会说。光看不练假把式,所以,如果真正要体会 sync over async,我们还需要自己动手进行测试:
先说明一下,在测试代码中,异步调用使用的是 HttpClient.GetAsync 方法,并且测试请求执行两次,关于具体的分析,后面再进行说明。
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test(); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static string Test(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = client.GetAsync(url).Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return response.Content.ReadAsStringAsync().Result; }}
输出结果:
Thread.CurrentThread.ManagedThreadId1:13Thread.CurrentThread.ManagedThreadId2:13Thread.CurrentThread.ManagedThreadId3:13Thread.CurrentThread.ManagedThreadId4:13Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6Thread.CurrentThread.ManagedThreadId3:6Thread.CurrentThread.ManagedThreadId4:6
简单总结:同步代码中调用异步,上面的测试代码应该是我们最常写的,为什么没有出现线程阻塞,页面卡死的情况呢?而且代码中调用了 GetAsync,为什么请求线程只有一个?后面再说,我们接着测试。
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Task.Run(() => Test2()).Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test2(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = await client.GetAsync(url); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
输出结果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:7Thread.CurrentThread.ManagedThreadId3:11Thread.CurrentThread.ManagedThreadId4:6Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:7Thread.CurrentThread.ManagedThreadId3:12Thread.CurrentThread.ManagedThreadId4:6
简单总结:根据上面的输出结果,我们发现,在一个请求过程中,总共会出现三个线程,一个是开始的请求线程,接着是 Task.Run 创建的一个线程,然后是异步方法中 await 等待的执行线程,需要注意的是,ManagedThreadId1 和 ManagedThreadId4 始终是一样的。
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test3().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test3(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var response = await client.GetAsync(url); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
输出结果:
Thread.CurrentThread.ManagedThreadId1:5Thread.CurrentThread.ManagedThreadId2:5
简单总结:首先,页面是卡死状态,ManagedThreadId3 并没有输出,也就是执行到 await client.GetAsync
的时候,线程就阻塞了。
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test4().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test4(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); return await Task.Run(() => { Thread.Sleep(1000); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return "xishuai"; });}
输出结果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6Thread.CurrentThread.ManagedThreadId3:7
简单总结:和第三种情况一样,页面也是卡死状态,但不同的是,ManagedThreadId3 是输出的,测试它的主要目的是和第三种情况形成对比,以便了解 HttpClient.GetAsync
中到底是什么鬼?
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test5().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}public static async Task<string> Test5(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId2:" + Thread.CurrentThread.ManagedThreadId); using (var client = new HttpClient()) { var task = client.GetAsync(url); var response = await task.ConfigureAwait(true); System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId3:" + Thread.CurrentThread.ManagedThreadId); return await response.Content.ReadAsStringAsync(); }}
输出结果:
Thread.CurrentThread.ManagedThreadId1:6Thread.CurrentThread.ManagedThreadId2:6
简单总结:和上面两种情况一样,页面也是卡死状态,它的效果和第三种完全一样,ManagedThreadId3 都没有输出的。
测试代码:
[Route("")][HttpGet]public string Index(){ System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId1:" + Thread.CurrentThread.ManagedThreadId); var result = Test6().Result; System.Diagnostics.Debug.WriteLine("Thread.CurrentThread.ManagedThreadId4:" + Thread.CurrentThread.ManagedThreadId); return result;}publ
新闻热点
疑难解答