-
async和await详解
1.非UI线程中执行
Test()函数带有async 和await ,返回值写成Task。
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 namespace _00_测试 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 Console.WriteLine($"Main Start:{Thread.CurrentThread.ManagedThreadId}"); 12 Task task = Test(); 13 Console.WriteLine($"Main End:{Thread.CurrentThread.ManagedThreadId}"); 14 Console.ReadKey(); 15 } 16 private async static Task Test() 17 { 18 Console.WriteLine($"当前主线程ID::{Thread.CurrentThread.ManagedThreadId}"); 19 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 20 Task task1 = Task.Factory.StartNew(() => 21 { 22 Thread.Sleep(100); 23 Console.WriteLine($"task1:{Thread.CurrentThread.ManagedThreadId}"); 24 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 25 }); 26 await task1; 27 28 Console.WriteLine($"task1 结束后的线程ID:{Thread.CurrentThread.ManagedThreadId}"); 29 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 30 31 await Task.Run(() => 32 { 33 Thread.Sleep(100); 34 Console.WriteLine($"task2:{Thread.CurrentThread.ManagedThreadId}"); 35 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 36 }); 37 Console.WriteLine($"Test End:{Thread.CurrentThread.ManagedThreadId}"); 38 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 39 } 40 } 41 }
上面是控制台应用程序,主线程的ID为1,第一个await和后面的代码都是子线程完成的。第二个await和后面的代码,也是子线程完成的。在非UI线程中执行的async异步方法,await等待的异步操作和后面要执行的代码,都是从线程池获取一个线程来执行的。
2.UI线程中执行
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading; 9 using System.Threading.Tasks; 10 using System.Windows.Forms; 11 12 namespace _009__数据库 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private async void button1_Click(object sender, EventArgs e) 22 { 23 Console.WriteLine($"Main Start:{Thread.CurrentThread.ManagedThreadId}"); 24 Task task = Test(); 25 await task; 26 Console.WriteLine($"Main End:{Thread.CurrentThread.ManagedThreadId}"); 27 Console.ReadKey(); 28 } 29 private async static Task Test() 30 { 31 Console.WriteLine($"当前主线程ID::{Thread.CurrentThread.ManagedThreadId}"); 32 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 33 Task task1 = Task.Factory.StartNew(() => 34 { 35 Thread.Sleep(100); 36 Console.WriteLine($"task1:{Thread.CurrentThread.ManagedThreadId}"); 37 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 38 }); 39 await task1; 40 41 Console.WriteLine($"task1 结束后的线程ID:{Thread.CurrentThread.ManagedThreadId}"); 42 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 43 44 await Task.Run(() => 45 { 46 Thread.Sleep(100); 47 Console.WriteLine($"task2:{Thread.CurrentThread.ManagedThreadId}"); 48 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 49 }); 50 Console.WriteLine($"Test End:{Thread.CurrentThread.ManagedThreadId}"); 51 Console.WriteLine($"是否是线程池线程:{Thread.CurrentThread.IsThreadPoolThread}"); 52 } 53 } 54 }
在UI线程中,async切换线程的规律和非UI线程不一样了。在UI线程中,await后面紧跟的代码,一直都是在UI线程中执行的。
注意:非UI线程中,await后面的动作都是子线程完成的;UI线程中,await后面的动作都是主线程完成的。
3.带返回值的异步方法
非UI线程
1 using System; 2 using System.Threading; 3 using System.Threading.Tasks; 4 5 namespace _00_测试 6 { 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 Console.WriteLine($"Main Start:{Thread.CurrentThread.ManagedThreadId}"); 12 Task<int> task = Test(); 13 Console.WriteLine($"结果为:{task.Result}"); 14 Console.WriteLine($"Main End:{Thread.CurrentThread.ManagedThreadId}"); 15 Console.ReadKey(); 16 } 17 private async static Task<int> Test() 18 { 19 int Value = 0; 20 Task task1 = Task.Factory.StartNew(() => 21 { 22 Value++; 23 Thread.Sleep(100); 24 }); 25 await task1; 26 return Value; 27 } 28 } 29 }
执行Test()异步方法,然后获取异步方法的返回值,执行异步方法的线程会一直阻塞,直到等到要获取的返回值。但是,在UI线程中,执行异步方案的是主线程,直接就死锁了。
UI线程
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Threading; 9 using System.Threading.Tasks; 10 using System.Windows.Forms; 11 12 namespace _009__数据库 13 { 14 public partial class Form1 : Form 15 { 16 public Form1() 17 { 18 InitializeComponent(); 19 } 20 21 private void button1_Click(object sender, EventArgs e) 22 { 23 Console.WriteLine($"Main Start:{Thread.CurrentThread.ManagedThreadId}"); 24 Task<int> task = Test(); 25 Console.WriteLine($"结果为:{task.Result}"); 26 Console.WriteLine($"Main End:{Thread.CurrentThread.ManagedThreadId}"); 27 Console.ReadKey(); 28 } 29 private async static Task<int> Test() 30 { 31 int Value = 0; 32 Task task1 = Task.Factory.StartNew(() => 33 { 34 Value++; 35 Thread.Sleep(100); 36 }); 37 await task1; 38 return Value; 39 } 40 } 41 }
在winform中,点击按钮,界面直接卡死了!!!
分析, 执行Test()异步方法,然后获取异步方法的返回值,但是在UI线程中,await后面的操作是UI线程执行的。那么,首先异步方法执行了await中的异步任务,UI线程已经开始等这个执行结果了,UI线程阻塞等待中;而await后面的
return Value;这一行,需要UI线程执行啊,此时UI线程阻塞等结果呢无法执行其他操作,就这么UI等返回值,子线程等UI线程等UI线程来执行 return Value;这行代码。谁也不让谁的等待下去,这就是死锁了。
原文链接:https://www.cnblogs.com/dfcq/p/16388646.html
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
SQL Server -- 解决存储过程传入参数作为s
关于JS定时器的整理
JS中使用Promise.all控制所有的异步请求都完
js中字符串的方法
import-local执行流程与node模块路径解析流程
检测数据类型的四种方法
js中数组的方法,32种方法
前端操作方法
数据类型
window.localStorage.setItem 和 localStorage.setIte
如何完美解决前端数字计算精度丢失与数