1.上下文 2.Async和Await简介 3.Await后续代码在哪个线程执行 4.Await 和同步上下文 5.Await究竟干了什么 1.上下文 想象一下: 你在不同的场合, 同样是"我饿了" , 意思完全不同 a. 在家说"我饿了" -> 妈妈给你做饭 b. 在朋友家说"我饿了" -> 朋友给你零食 同样一句"我饿了" , 因为说的时候环境不同, 结果完全不同编程中的上下文就是当前代码能有的所有东西( 变量, 方法, 类, 资源, 环境信息) 的集合, 就 像你在厨房, 上下文就是厨房里的工具( 刀, 锅, 食材) , 你能做饭public class Example { private string name= "全局名字" ; // 这是类的上下文(成员变量) public void Method1 ( ) { string name= "局部名字" ; // 这是方法的上下文(局部变量) Console. WriteLine ( name) ; // 输出“局部名字” // 因为当前方法上下文中有name,就先用它 } public void Method2 ( ) { Console. WriteLine ( name) ; // 输出“全局名字” // 方法上下文没有name,就用类的上下文中的name } } 同步上下文在异步操作后, 默认会回到原来的上下文// UI程序中,控制回到UI线程执行 private async void Button_Click ( object sender, EventArgs e) { // 异步操作前:UI线程上下文 await Task. Delay ( 1000 ) ; // 异步操作后:默认会回到原来的上下文(UI线程) label. Text= "完成" ; // 安全访问UI控件 } // 指定不捕获上下文(提高性能) await Task. Delay ( 1000 ) . ConfigureAwait ( false ) ; // 这里不会回到UI线程上下文 2.Async和Await简介 async 与await 用来简化异步编程, 让异步代码像同步代码一样, 易于理解 a. async 关键字用于修饰一个方法, 表示该方法是异步的; 异步方法的返回类型通常是Task, Task< T> b. 异步方法内部, 可以使用await 关键字来等待一个异步操作( Task) , 而不会阻塞当前线程 c. 使用await 关键字的方法必须用async 修饰// 1. 声明异步方法:在方法前加 async public async Task< string > 下载网页( ) { // 2. 使用 await 等待异步操作完成 string 内容= await 网络下载方法( ) ; // 3. await 后面的代码会等待,但不会阻塞线程 Console. WriteLine ( "下载完成" ) ; return 内容; // 自动包装成 Task<string> } 3.Await后续代码在哪个线程执行 a. 当初始的await 被调用时, 异步函数内部的代码仍在与调用线程相同的线程上运行 b. 后续的await 延续内容可能在不同的线程池中执行, 取决于线程池4.Await 和同步上下文 在C#异步编程中, await 关键字用于挂起当前方法, 直到等待的异步操作完成; 当异步操作完成 后, 方法会尝试恢复执行同步上下文( Synchronization Context) 是一个抽象, 它代表了一个线程的上下文环境; 在 Windows Forms、WPF、ASP. NET等应用程序模型中, 都有一个特定的同步上下文, 它通常与 UI线程相关联, 用于确保代码在正确的线程上执行( 如: UI控件的更新必须在UI线程上进行) 当我们使用await 时, 默认情况下, await 会捕获当前的同步上下文, 并在该同步上下文中恢复 执行; 这意味着, 如果在UI线程上使用await , 那么await 之后的代码将会在UI线程上执行, 这样我们就可以安全地更新UI控件ConfigureAwait ( false ) 来告诉await 不需要捕获原始同步上下文, 而是在线程池上下文中恢 复执行private async void button1_Click ( object sender, EventArgs e) { // 这里在UI线程上执行 await SomeAsyncMethod ( ) ; // 这里也会在UI线程上执行,所以可以安全更新UI label1. Text= "Done" ; } 5.Await究竟干了什么 当我们使用async 和await 时, c#编译器将异步方法转换成一个状态机; 状态机将异步方法分成 了多个步骤, 每一个await 都是一个状态; 当遇到await 时, 如果等待的任务尚未完成, 状态机 会将方法挂起( 返回) , 并注册一个续延( continuation) , 当任务完成后, 再调用续延来恢复 方法的执行( 从上次暂停的地方继续) 1 ) . 原始代码async Task 示例方法( ) { Console. WriteLine ( "开始" ) ; int a= 10 ; string result= await 网络请求( ) ; // 第一个await a= a+ 5 ; Console. WriteLine ( $"结果: { result } , a= { a } " ) ; await Task. Delay ( 1000 ) ; // 第二个await Console. WriteLine ( "完成" ) ; } 2 ) . 编译器把它变成状态机// 伪代码,展示状态机原理 class 状态机{ int 状态= 0 ; // 0=刚开始,1=第一个await后,2=第二个await后 int a; string result; void 继续执行( ) { switch ( 状态) { case 0 : // 第一次执行 Console. WriteLine ( "开始" ) ; a= 10 ; 发起的网络请求= 开始网络请求( ) ; 状态= 1 ; // 挂起,等网络请求完成 break ; case 1 : // 网络请求完成后继续 result= 获取网络请求结果( ) ; a= a+ 5 ; Console. WriteLine ( $"结果: { result } , a= { a } " ) ; 开始延迟( ) ; 状态= 2 ; // 挂起,等延迟完成 break ; case 2 : // 延迟完成后继续 Console. WriteLine ( "完成" ) ; break ; } } }