C# Async与Await用法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CloudLearning.UI
{
    class Program
    {
        static void Main(string[] args)
        {
            AsyncPrintHelloWorld();
            Console.ReadLine();
        }

        public async static void AsyncPrintHelloWorld()
        {
            Console.WriteLine("异步方法调用开始");  
            var result1 = TMothd1();
            var result2 = TMothd2();
            var result3 = TMothd3();
            Console.WriteLine("异步方法完成");
            int r1 = await result1;
            int r2= await result2;
            int r3 = await result3;
            Console.WriteLine("{0},{1},{2}", r1, r2, r3);
        }

        public async static Task<int> TMothd1()
        {
            return awa
<think>我们正在讨论C#中的asyncawait关键字。根据用户提供的引用,我们可以总结如下: 引用[1]说明:使用async修饰一个方法,表示这是一个异步方法。我们可以调用C# 4.5中已有的异步方法,也可以使用Task来定义一些耗时的操作作为异步方法。但是,仅仅使用async和返回Task并不会开启新线程,也不会使方法自动成为不阻塞的异步方法。 引用[2]说明:await运算符会暂停其所属的async方法的执行,直到其操作数(通常是一个Task)表示的异步操作完成。异步操作完成后,await会返回操作的结果(如果有)。如果操作已经完成,则立即返回结果,不会暂停方法。await不会阻塞线程,当它暂停异步方法时,控制权会返回给调用方。 因此,我们可以这样理解: - async关键字用于标记一个方法为异步方法。异步方法通常返回Task、Task<T>或ValueTask<T>(对于可能有结果的情况)或者void(但通常不推荐,主要用于事件处理程序)。 - await关键字用于等待一个异步操作完成。它只能在async方法内部使用。 下面我们详细介绍asyncawait用法。 ### 1. 定义异步方法 使用async关键字修饰方法,并且返回类型为Task(无返回值)、Task<T>(返回T类型)或ValueTask<T>(和Task<T>类似,但用于可能从同步路径返回的情况,减少分配开销)。 例如: ```csharp public async Task<string> GetStringAsync() { // 模拟异步操作 await Task.Delay(1000); // 等待1秒 return "Hello, World!"; } ``` ### 2. 调用异步方法 调用异步方法时,通常使用await关键字来等待其完成,并获取结果(如果有)。调用异步方法的方法也必须声明为async。 例如: ```csharp public async Task CallAsync() { string result = await GetStringAsync(); Console.WriteLine(result); } ``` ### 3. 注意事项 - 异步方法中通常包含至少一个await表达式。如果没有,那么该方法将同步执行(但编译器会给出警告)。 - 避免在异步方法中使用阻塞操作(如Task.Wait()或Task.Result),这可能导致死锁。 - 在UI线程中,await会返回UI线程上下文,因此可以直接更新UI。但在某些情况下(如ASP.NET Core),默认没有同步上下文,因此不会回到原始上下文。 - 对于I/O密集型操作(如文件读写、网络请求),使用异步方法可以释放线程,提高系统吞吐量。 - 对于CPU密集型操作,可以使用Task.Run将其放入线程池中执行。 ### 4. 异常处理 异步方法中的异常会被捕获并存储在返回的Task对象中。当await该任务时,异常会被重新抛出。 例如: ```csharp public async Task ThrowExceptionAsync() { await Task.Delay(1000); throw new InvalidOperationException("An error occurred."); } public async Task HandleExceptionAsync() { try { await ThrowExceptionAsync(); } catch (InvalidOperationException ex) { Console.WriteLine(ex.Message); } } ``` ### 5. 组合异步操作 可以使用Task.WhenAll和Task.WhenAny来组合多个异步操作。 例如: ```csharp public async Task MultipleAsync() { Task<string> task1 = GetStringAsync(); Task<string> task2 = GetStringAsync(); string[] results = await Task.WhenAll(task1, task2); // 处理结果 } public async Task AnyAsync() { Task<string> task1 = GetStringAsync(); Task<string> task2 = GetStringAsync(); Task<string> firstFinished = await Task.WhenAny(task1, task2); string result = await firstFinished; // 处理最先完成的任务的结果 } ``` ### 6. 自定义异步方法 如果我们需要将一个同步的耗时操作包装成异步方法,可以使用Task.Run(将其放入线程池)或者TaskCompletionSource(用于更复杂的场景)。 例如: ```csharp public Task<int> CalculateAsync(int n) { return Task.Run(() => { // 模拟耗时计算 Thread.Sleep(1000); return n * n; }); } ``` ### 7. 避免死锁 在UI应用程序(如WinForms、WPF)或ASP.NET(非Core)中,如果在同步代码中阻塞等待异步方法(如使用.Result或.Wait()),可能导致死锁。因此,尽量使用async/await“一路到底”。 错误示例: ```csharp // 在UI线程中调用,会导致死锁 public void Deadlock() { var result = GetStringAsync().Result; // 阻塞等待,但异步方法需要返回UI线程完成,而UI线程被阻塞了 } ``` 正确做法: ```csharp public async Task NoDeadlockAsync() { var result = await GetStringAsync(); } ``` ### 总结 asyncawaitC#中异步编程的核心,它们使得异步代码的编写和阅读更接近于同步代码,同时提高了应用程序的响应能力和吞吐量。正确使用异步编程可以避免阻塞主线程(UI线程或服务器线程),充分利用系统资源。 §§相关问题§§ 1. async方法返回void会产生什么问题? 2. 如何在ASP.NET Core中正确处理异步操作? 3. 什么是同步上下文(SynchronizationContext)?它在异步编程中起什么作用? 4. 异步编程中,如何取消一个正在执行的异步操作? 5. ValueTask和Task有什么区别?在什么情况下使用ValueTask? </think>以下是关于 C# 中 `async` 和 `await` 关键字的详细说明: ### 1. 基本概念 - **`async`**:修饰方法,表示该方法包含异步操作 ```csharp public async Task MyAsyncMethod() { ... } ``` - **`await`**:暂停当前异步方法的执行,等待异步操作完成 ```csharp await SomeAsyncOperation(); ``` ### 2. 核心特性 1. **非阻塞执行**: `await` 不会阻塞调用线程,控制权返回给调用方[^2] 2. **上下文恢复**: 异步操作完成后自动恢复原始执行上下文(如 UI 线程) 3. **异常处理**: 使用 `try/catch` 捕获异步操作中的异常 ### 3. 标准使用模式 ```csharp public async Task<int> DownloadDataAsync(string url) { using (HttpClient client = new HttpClient()) { // 暂停方法直到下载完成,但不阻塞线程 string result = await client.GetStringAsync(url); return result.Length; } } ``` ### 4. 返回值类型 | 返回类型 | 适用场景 | |----------------|----------------------------| | `Task` | 无返回值的异步操作 | | `Task<T>` | 返回类型为 T 的异步操作 | | `ValueTask<T>` | 高性能场景(.NET Core 2.1+)| ### 5. 执行流程示例 ```mermaid graph LR A[调用异步方法] --> B[遇到await] B --> C[返回未完成Task给调用方] C --> D[异步操作在后台执行] D --> E[操作完成时恢复方法执行] ``` ### 6. 重要注意事项 1. **避免 `async void`**: 仅在事件处理程序中允许,否则无法捕获异常 ```csharp // 错误示例(除事件外) public async void BadPractice() { ... } ``` 2. **配置上下文控制**: 使用 `ConfigureAwait(false)` 避免上下文恢复 ```csharp await SomeOperation().ConfigureAwait(false); ``` 3. **组合异步操作**: 使用 `Task.WhenAll` 并行执行多个异步操作 ```csharp var task1 = Operation1Async(); var task2 = Operation2Async(); await Task.WhenAll(task1, task2); ``` ### 7. 常见错误 ```csharp // 错误:阻塞异步操作导致死锁 var result = GetDataAsync().Result; // 正确:异步等待 var result = await GetDataAsync(); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍狼_2001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值