C# await async lambda

本文详细探讨了C#中await和async关键字在异步编程中的应用,包括如何创建异步方法,理解任务的完成状态,以及lambda表达式在异步编程中的角色。通过实例展示了如何利用await和async提高程序的响应速度,同时避免阻塞主线程。
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            ssxxx.Text = "Start";

            Task<int> task =   Task<int>.Factory.StartNew( () => {
                int sum = 0; 
                for (int i = 10; i < 1000000; i++)
                {
                    sum += i;
                
                }

                return sum;
            });


 


            var ret = await task;

            this.ssxxx.Text = ret.ToString();
        }
//另外一个测试

     private async void Button_Click(object sender, RoutedEventArgs e)
        {
            ssxxx.Text = "Start";

            Task<int> task = mywaitFunc(1100);

            var ret = await task;

            this.ssxxx.Text = ret.ToString();
        }


        private async Task<int> mywaitFunc(int iii)
        {
            Task<int> task=  Task.Run(() =>
            {
                int sum = 0;
                for (int i = 10; i < iii; i++)
                {
                    sum += i;
                }

                return sum;

            });

            int su3m = await task;
            return su3m;

            
        
        }
<think>我们正在讨论C#中的asyncawait的工作原理。根据之前的对话历史和引用的资料,用户希望了解其使用原理。 引用[1]提到async/await简化了异步编程,但需要深入理解其底层机制;引用[2]提到控制流如何在异步方法之间移动;引用[3]展示了一个例子,说明await如何释放UI线程并在操作完成后回到原上下文执行。 核心原理: 1. async关键字标记的方法会被编译器重写为一个状态机。 2. await操作符会检查等待的操作是否完成。如果完成,则同步继续执行;否则,将方法挂起,返回控制权给调用者,并注册一个续体(continuation)以便在操作完成后恢复执行。 3. 当异步操作完成时,续体将被调度执行(可能是回到原始上下文,如UI线程,也可能在线程池线程中,取决于配置)。 状态机的工作流程: - 编译器将async方法拆分成多个片段,每个await点都是一个潜在的分割点。 - 状态机维护当前执行的位置(状态)以及局部变量等上下文。 执行流程示例(引用[3]的例子): 用户点击按钮(UI线程) -> 执行DownloadButton_Click()(UI线程) -> 调用DownloadFileAsync()(UI线程) -> 遇到第一个await(client.GetAsync),检查操作是否完成 如果未完成,则挂起方法,返回一个未完成的Task给调用者(DownloadButton_Click) 同时,注册续体:当GetAsync完成时,继续执行DownloadFileAsync的后续代码(可能会回到UI线程,因为捕获了SynchronizationContext) -> UI线程被释放,可以响应其他操作 -> 后台线程完成网络请求 -> 触发续体:调度继续执行(在UI线程上)DownloadFileAsyncawait之后的代码 -> 继续执行下一个await(循环内的ReadAsync/WriteAsync)同样处理 注意:await不会阻塞线程,它只是将方法挂起,让出当前线程。 我们还可以用状态图来表示: ```mermaid stateDiagram-v2 [*] --> 方法开始 方法开始 --> 遇到第一个await: 执行await之前的代码 遇到第一个await --> 检查是否完成 检查是否完成 --> 已完成: 是 检查是否完成 --> 未完成: 否 已完成 --> 继续执行后续代码 未完成 --> 挂起方法并返回Task 挂起方法并返回Task --> [*] 继续执行后续代码 --> 下一个await或结束 下一个await或结束 --> [*] 异步操作完成事件 --> 恢复执行 恢复执行 --> 继续执行后续代码 ``` 续体的调度: 当异步操作完成时,续体(即await之后的代码)会被调度执行。调度方式取决于调用方法时的上下文: - 如果在UI线程上调用,且没有显式配置(ConfigureAwait(true)),则续体会在UI线程上执行(通过SynchronizationContext.Post)。 - 如果配置了ConfigureAwait(false),则续体可能在线程池线程上执行,不保证回到原始上下文。 状态机的实现细节: 编译器会为async方法生成一个状态机结构(struct)或类(class,如果方法是非静态的或在lambda表达式中使用),其中: - 包含状态字段(记录当前执行到哪个位置) - 包含局部变量字段(保存方法的局部变量) - 包含一个TaskCompletionSource或类似的机制来返回Task - 实现IAsyncStateMachine接口(包含MoveNext和SetStateMachine方法) 在方法首次调用时,状态机初始化状态(通常为-1),然后调用MoveNext方法开始执行。当遇到await时,状态机记录位置(状态值),然后返回。当异步操作完成,它会调用MoveNext方法,状态机从记录的状态值跳转到相应的位置继续执行。 示例代码(伪代码形式): ```csharp // 原始async方法 public async Task MyMethodAsync() { Console.WriteLine("Before first await"); await Task.Delay(1000); Console.WriteLine("After first await"); } // 编译器生成的状态机大致如下(简化): class MyMethodAsyncStateMachine : IAsyncStateMachine { public int state = -1; // 状态 public AsyncTaskMethodBuilder builder = AsyncTaskMethodBuilder.Create(); private TaskAwaiter awaiter; void MoveNext() { try { if (state == -1) { Console.WriteLine("Before first await"); awaiter = Task.Delay(1000).GetAwaiter(); if (!awaiter.IsCompleted) { state = 0; // 更新状态,表示下一次从第一个await之后开始 builder.AwaitUnsafeOnCompleted(ref awaiter, ref this); return; // 挂起,返回任务 } } // 状态0表示从第一个await之后恢复 if (state == 0) { // 这里不再需要获取awaiter,因为已经有了 } awaiter.GetResult(); // 确保操作完成(可能重新抛出异常) Console.WriteLine("After first await"); builder.SetResult(); // 标记任务完成 } catch (Exception ex) { builder.SetException(ex); } } } ``` 注意:实际生成的代码比这复杂,但原理一致。 总结: async/await的实现依赖于编译器生成的状态机,该状态机在遇到await时挂起方法,并将后续代码包装为续体,在异步操作完成时调度执行。整个过程不阻塞线程,提高了程序的响应性和资源利用率。 相关问题: 1. 如何在async方法中正确处理异常? 2. 使用ConfigureAwait(false)有什么好处? 3. 在控制台应用程序和ASP.NET Core中使用async/await有什么不同? </think>### C# async/await 使用原理深度解析 async/awaitC# 实现异步编程的核心机制,其底层运作基于**编译器生成的状态机模型**。以下是核心原理分步说明: #### 1. **编译器状态机转换原理** 🔄 当方法标记 `async` 时,编译器会将其重写为**状态机结构**(实现 `IAsyncStateMachine` 接口): ```csharp // 原始代码 public async Task DownloadAsync() { await client.GetAsync(url); await ReadDataAsync(); } // 编译器生成的状态机伪代码 class StateMachine : IAsyncStateMachine { int state = -1; // 执行位置标记 TaskAwaiter awaiter; void MoveNext() { switch (state) { case -1: awaiter = client.GetAsync(url).GetAwaiter(); if (!awaiter.IsCompleted) { state = 0; // 记录断点位置 awaiter.OnCompleted(MoveNext); // 注册回调 return; // 返回控制权 } goto case 0; case 0: awaiter.GetResult(); // 获取结果 // 继续执行下一行代码... } } } ``` - **状态字段 `state`**:精确记录代码执行位置(类似程序计数器)[^1] - **回调注册**:通过 `awaiter.OnCompleted()` 实现异步操作完成后的续体(continuation)执行[^2] #### 2. **await 关键操作流程** ▶️ ```mermaid sequenceDiagram participant Caller as 调用线程 participant StateMachine as 状态机 participant AsyncOp as 异步操作(如I/O) Caller->>StateMachine: 调用async方法 StateMachine->>AsyncOp: 启动异步操作 AsyncOp->>StateMachine: 返回未完成Task StateMachine->>Caller: 返回控制权(线程释放) Note over Caller: 线程可执行其他任务 AsyncOp-->>StateMachine: 操作完成信号 StateMachine->>Caller: 请求线程上下文 Caller->>StateMachine: 恢复执行(续体) ``` 1. **挂起检查**:遇到 `await` 时检查异步操作状态 - 若已完成:**同步继续执行**(不释放线程) - 若未完成:**挂起当前方法**,返回未完成的 `Task` 2. **线程释放**:控制权返回调用方,**释放当前线程**(UI线程/线程池) 3. **续体注册**:向异步操作注册回调方法(编译器生成) 4. **恢复执行**:异步操作完成后,**调度器触发续体**在原上下文恢复执行[^3] #### 3. **核心设计特性** ⚙️ | 特性 | 原理说明 | |---------------------|--------------------------------------------------------------------------| | **非阻塞性** | 通过状态机保存执行上下文,释放当前线程 | | **上下文流动** | 默认捕获 `SynchronizationContext` 确保续体在原始上下文执行(如UI线程) | | **异常传播** | 异步操作异常通过 `Task.Exception` 传递到 `await` 点抛出 | | **值任务优化** | `ValueTask` 避免堆分配,适合高频调用的异步操作 | #### 4. **典型执行场景对比** 🔍 ```csharp // 同步阻塞版本 ❌ void DownloadSync() { var data = client.Download(url); // 阻塞线程 Process(data); } // 异步非阻塞版本 ✅ async Task DownloadAsync() { var data = await client.DownloadAsync(url); // 挂起点 Process(data); // 恢复点 } ``` - **同步阻塞**:线程在 `Download()` 期间完全被占用(资源浪费) - **异步非阻塞**:数据下载期间线程可处理其他任务(如UI响应) #### 5. **性能优化实践** 🚀 ```csharp // 避免上下文捕获(后台任务) async Task ProcessDataAsync() { await ReadFileAsync().ConfigureAwait(false); // 放弃上下文捕获 // 此处在线程池执行 HeavyComputation(); // 需回UI线程时显式切换 Dispatcher.Invoke(() => UpdateUI()); } ``` - `ConfigureAwait(false)`:阻止上下文流动,**减少线程切换开销**[^1] - `ValueTask`:替代 `Task` 减少堆分配,适合结果常同步返回的操作 > 关键结论:async/await **不是多线程技术**,而是通过状态机实现的**非阻塞执行控制流**。其核心价值在于用同步写法实现异步操作,同时保证线程高效利用[^2][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值