C# 之 Task、async和 await 、Thread 的简单整理

C#async/await详解:简化异步编程与Task、线程池的关系
本文介绍了C#5.0中的async和await特性,如何通过这些关键字创建异步方法,以及它们与Task、线程池之间的关系。重点讲解了async/await的工作原理,如何使用异步读取文件,并提到了Task的不同返回类型及其应用场景。

1、异步方法(async/await)

在 C# 5.0 中出现的 async 和 await ,让异步编程变得更简单。

此方法利用了 .NET Framework 4.5 及更高版本、.NET Core 和 Windows 运行时中的异步支持。

编译器可执行开发人员曾进行的高难度工作,且应用程序保留了一个类似于同步代码的逻辑结构。

async 用在方法定义前面,await只能写在带有async标记的方法中。

注意await异步等待的地方,await后面的代码和前面的代码执行的线程可能不一样

async关键字创建了一个状态机,类似yield return 语句;await会解除当前线程的阻塞,完成其他任务

这里实现同步异步读取文件内容的方式。

我们可以看到异步读取代码和同步读取代码基本一致。async/await让异步编码变得更简单,我们可以像写同步代码一样去写异步代码。

注意一个小问题:异步方法中方法签名返回值为Task,代码中的返回值为T。

代码中GetContentAsync的签名返回值为Task,而代码中返回值为string。牢记这一细节对我们分析异步代码很有帮助。

异步方法签名的返回值有以下三种:

① Task:如果调用方法想通过调用异步方法获取一个T类型的返回值,那么签名必须为Task;

② Task:如果调用方法不想通过异步方法获取一个值,仅仅想追踪异步方法的执行状态,那么我们可以设置异步方法签名的返回值为Task;

③ void:如果调用方法仅仅只是调用一下异步方法,不和异步方法做其他交互,我们可以设置异步方法签名的返回值为void,这种形式也叫做“调用并忘记”。

参考代码如下

        static void Main(string[] args)
        {
            //Console.WriteLine(" Environment.CurrentDirectory "+ Environment.CurrentDirectory);
            string content = GetContentAsync(Environment.CurrentDirectory + @"/test.txt").Result;
            //调用同步方法
            //string content = GetContent(Environment.CurrentDirectory + @"/test.txt");
            Console.WriteLine(content);
            Console.ReadKey();
        }
 
        /// <summary>
        /// 异步读取文件内容
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        async static Task<string> GetContentAsync(string filename)
        {
 
            FileStream fs = new FileStream(filename, FileMode.Open);
            var bytes = new byte[fs.Length];
            //ReadAync方法异步读取内容,不阻塞线程
            Console.WriteLine("开始读取文件");
            int len = await fs.ReadAsync(bytes, 0, bytes.Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }
 
        /// <summary>
        /// 同步读取文件内容
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        static string GetContent(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Open);
            var bytes = new byte[fs.Length];
            //Read方法同步读取内容,阻塞线程
            int len = fs.Read(bytes, 0, bytes.Length);
            string result = Encoding.UTF8.GetString(bytes);
            return result;
        }

2、Task、async和 await 、Thread 简单小结

最后我们简单记住一些特性,就可以有个较好的理解他们了:

1)async/await是基于Task的

2)而Task是对ThreadPool的封装改进,主要是为了更有效的控制线程池中的线程(ThreadPool中的线程,我们很难通过代码控制其执行顺序,任务延续和取消等等);

3)ThreadPool基于Thread的,主要目的是减少Thread创建数量和管理Thread的成本。

4)async/await Task是C#中更先进的,也是微软大力推广的特性

5)我们在开发中可以尝试使用Task来替代Thread/ThreadPool,处理本地IO和网络IO任务是尽量使用async/await来提高任务执行效率。

<think> 我们被要求用中文解释C#Taskasyncawait关键字,并在最后提出3-5个相关问题,且必须以“§§相关问题§§”开头。 注意:回答完问题后,必须在末尾输出“§§相关问题§§”并紧接着列出3-5个相关问题,禁止换行分散。 解释Taskasyncawait: 1. Task:表示一个异步操作,通常用于后台执行耗时操作,避免阻塞主线程。 2. async:修饰方法,表明该方法内部包含异步操作,通常与await一起使用。async方法返回类型通常是TaskTask<T>或void(不推荐void)。 3. await:用于等待一个异步操作完成,它不会阻塞当前线程,而是将控制权返回给调用方,直到异步操作完成再继续执行后续代码。 我们将按照要求进行回答,最后提出相关问题。 </think> ### C# 的 `Task`、`async` 及 `await` 关键字详解 #### **1. Task(任务)** - **本质**:表示一个异步操作,是 `System.Threading.Tasks` 命名空间的核心类。 - **作用**: - 封装异步操作(如 I/O、网络请求、CPU 密集型计算) - 提供任务状态监控(`Status` 属性:`RanToCompletion`/`Canceled`/`Faulted`) - 支持结果返回(`Task<TResult>` 泛型版本) - **关键特性**: ```csharp // 创建并启动任务 Task.Run(() => { Thread.Sleep(1000); // 模拟耗时操作 return 42; // 返回结果 }); ``` #### **2. async(异步修饰符)** - **作用**:标记方法为**异步方法**,允许在方法内使用 `await`。 - **规则**: - 必须返回 `Task`、`Task<T>` 或 `ValueTask` - `void` 仅用于事件处理(不推荐) - **示例**: ```csharp public async Task<int> GetDataAsync() { // 可包含 await 操作 return 42; } ``` #### **3. await(异步等待)** - **机制**: - 暂停当前方法执行,**立即将控制权返还给调用方** - 后台异步等待任务完成(不阻塞线程) - 任务完成后**恢复执行**(可能在不同线程) - **本质**:编译器将方法重写为**状态机**(通过 `IAsyncStateMachine`)。 - **示例**: ```csharp public async Task ProcessDataAsync() { int data = await GetDataAsync(); // 暂停并异步等待 Console.WriteLine(data); // 完成后恢复执行 } ``` --- ### **工作流程解析** ```mermaid flowchart LR A[调用异步方法] --> B[遇到 await] B --> C{任务是否完成?} C -- 是 --> D[同步继续执行] C -- 否 --> E[挂起方法] E --> F[返回 Task 给调用方] F --> G[任务完成时唤醒] G --> H[恢复执行后续代码] ``` --- ### **关键特性对比** | 特性 | `Task` | `async/await` | |---------------------|--------------------------------|-------------------------------------------| | **线程阻塞** | ❌ 非阻塞 | ❌ 非阻塞 | | **线程资源** | 使用线程池线程 | 不占用线程(I/O 操作) | | **异常处理** | 通过 `AggregateException` 封装 | 直接用 `try/catch` 捕获 | | **状态管理** | 手动监控 `Status` | 自动通过状态机管理 | --- ### **最佳实践** 1. **避免 `async void`** (除事件处理外),否则无法捕获异常: ```csharp // 错误示例 public async void LoadData() { ... } // 正确示例 public async Task LoadDataAsync() { ... } ``` 2. **配置上下文** 使用 `ConfigureAwait(false)` 避免死锁(非 UI 场景): ```csharp var data = await GetDataAsync().ConfigureAwait(false); ``` 3. **并行优化** 组合多个任务加速执行: ```csharp var task1 = DownloadAsync(url1); var task2 = DownloadAsync(url2); await Task.WhenAll(task1, task2); // 并行等待 ``` 4. **取消操作** 通过 `CancellationToken` 实现超时控制: ```csharp var cts = new CancellationTokenSource(5000); // 5秒超时 await ProcessAsync(cts.Token); ``` --- ### **底层原理** 1. **状态机转换**:编译器将 `async` 方法拆解为: - `MoveNext()` 方法管理执行状态 - 本地变量提升为类字段 2. **线程池调度**:`Task` 默认使用线程池线程 3. **同步上下文**:`await` 完成后默认返回原始上下文(如 UI 线程)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值