Async Await 工作流程和作用

本文深入探讨了C#中Async与Await的使用规则,通过对比传统Thread方式,展示了Async与Await如何简化多线程编程,实现响应式程序,避免界面卡顿,同时保持代码逻辑的清晰连贯。

从多线程最常用的场景模式说起。

比如下面的一段代码, 函数中需要从远程获取数据, 这个过程可能需要10多秒或者更长,甚至网络可能断了。那么调用这个函数的界面将“死卡”住。

 

只要编程语言支持多线程,这个问题便可以解决。便能构建所谓的响应式程序。所以很多文档说async, await 是为了构建响应式程序。在我看来,并不如此,就是在c# 支持Task之前,就可以使用比较底层的Thread对象,但是需要创建线程,启动线程,停止线程等操作。除了这些步骤之后,因为线程是内核对象,管理不善,很容易在程序退出时还驻留于系统中,导致程序并不是完全地退出。

C#在 C# 4.0引入Task,它提供了一种比thread更简单的方式来使用多线程。但是如何得到Task所在的线程的结果,方式是非常有限的。而有了async,await之后,使用多线程形式便如同单线程一样简单,并且安全有保障。

但是这个“简单”是有条件,在真正理解多线程程序的写法及运行以及明白 async, await的使用规则后,这个新语法才能给你“精美高效”的感觉。

多线程的实质及原理就不说,网上有太多说得比我好,我学习多线程也做过一些笔记“  https://blog.youkuaiyun.com/mochounv/article/details/52217297 ”。

现在谈async, await使用的基本规则。

一、async, await的工作流

 

在使用传统的Thread,我们会把第2步中的耗时语句移到线程函数中。为了使程序按以1、2、3的顺序执行,且界面不会卡顿,这时我们需要把这语句封装到一个函数,作为线程函数,将第3步的语句封装成另一个函数,作为线程序函数执行完成的一个回调函数。一个本应一气呵成的逻辑因为多线程的语法限制,分离于程序的各个部分。

二、 async, await 的作用

有了async, await以后,以上代码可以在成以下样的

 

整个逻辑紧密相关的代码写在一起,但是界面不再卡断了。

这就是async, await的功劳。当一个函数中有需要在非当前线程执行的代码时,可以使用async 来修饰这个函数,对于需要放到非当前线程的语句,需要在前面添加await。

Await 关键字会使得当前线程停止执行往下代码(注意,但是当前线程并没有停止),当前线程继续接收用户的其它输入,所以界面就不会卡住;

只有当await得到可用的结果时,当前线程才会把剩下的没有执行的代码执行。可见,await 除了启动新线程,所以对开发人员来说,编写代码的方式不需要因为不同线程切换上、下文而把本应相关的代码分散各处。

那么,如果单独async 修饰方法名时,是否也能得到这个效果呢,根据以上分析不难看出,起异步作用的是await。所以显然是不行。以上代码,如果仅仅使用async,那么界面依然会卡顿。

如果我们在方法修饰中,把async task改为void,编译器会报错告诉我们

Error        CS4033        The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'。

先不管return type "task",至少很明确,使用await的时一定要使用async.

综上所述,async,await的结合使用,隐藏了不同线程切换上下文的细节,开发者可以像编写单线程同步代码一样写多线程代码。

 

在下一篇中讲 如何使用

<think>嗯,用户问的是C#中async/awaitawait有什么作用。我需要先回忆一下async/await的基本概念,然后重点解释await作用。可能用户对异步编程不太熟悉,所以得从基础讲起,但也不能太冗长。 首先,asyncawait是C#处理异步操作的关键字,特别是用来简化异步编程的复杂性。await作用应该是在异步方法中挂起当前方法的执行,直到等待的任务完成,同时释放线程资源,避免阻塞。需要说明它如何让代码看起来像同步执行,但实际上是非阻塞的。 可能用户想知道为什么用await而不是同步等待,或者它背后的机制。需要提到状态机的生成,编译器如何将async方法转换为状态机,以及await如何让线程池线程去处理其他任务,提高应用程序的响应性,尤其是在UI或服务端应用中。 还要区分IO密集型CPU密集型操作的适用场景,可能await主要用于IO操作,比如文件读写、网络请求等,这时候不需要占用线程,而是通过回调机制通知完成。 可能用户会遇到死锁问题,比如在同步代码中调用异步方法时,如果错误地使用.Result或.Wait(),而上下文被阻塞,导致死锁。这时候需要说明ConfigureAwait(false)的作用,或者建议避免混合使用同步异步代码。 另外,需要强调await不会阻塞当前线程,而是将方法的剩余部分作为回调,当任务完成后继续执行。这样主线程在UI应用中不会被卡住,保持界面的流畅。 可能还要对比之前的异步模式,比如Begin/End或者事件驱动的EAP,说明async/await的优势,代码更简洁易读,错误处理更方便,用try-catch就能处理异常。 最后,给出一个简单的例子,比如用HttpClient访问网络资源,展示async/await的用法,说明在await时,控制权交回,直到请求完成恢复执行。 需要检查是否有遗漏的点,比如返回值类型Task或Task<T>,async方法的结构,以及await只能用在async方法中这些限制。不过用户主要问的是await作用,可能不需要深入这些细节,但适当提及可以更全面。 总结一下,await作用包括非阻塞等待、释放线程资源、简化异步代码、提高程序响应性等。需要分点说明,结构清晰,让用户逐步理解。</think>好的,我来逐步解释C#中`await`关键字的作用: 1. **非阻塞等待** - `await`用于异步方法中,表示「等待一个异步操作完成」,但不会阻塞当前线程。 - 示例: ```csharp var data = await httpClient.GetStringAsync("https://xxx.com"); ``` 这里线程会释放去处理其他任务,直到HTTP请求完成 2. **代码结构简化** - 传统异步需回调嵌套,`await`使代码保持线性结构,如同步代码般易读 - 对比旧模式: ```csharp // 传统Begin/End模式 request.BeginGetResponse(result => { var response = request.EndGetResponse(result); // 处理回调... }, null); // await模式 var response = await request.GetResponseAsync(); // 直接继续执行 ``` 3. **线程资源管理** - 在UI线程中:避免界面卡死 - 在服务端:提高线程池利用率 - 原理:遇到`await`时,会将方法剩余部分打包为「续延任务」(continuation),待异步操作完成后自动调度执行 4. **异常处理** - 允许使用标准`try/catch`捕获异步操作中的异常 - 示例: ```csharp try { await SomeAsyncOperation(); } catch (Exception ex) { // 处理异常 } ``` 5. **上下文保持** - 默认会捕获同步上下文(如UI线程),使得`await`完成后自动回到原上下文 - 可通过`ConfigureAwait(false)`关闭该行为提升性能 **典型应用场景**: - 网络请求(HTTP/数据库) - 文件I/O操作 - 耗时计算(配合`Task.Run`) **重要限制**: - 必须在`async`修饰的方法中使用 - 等待对象需是`awaitable`类型(如`Task`/`Task<T>`) **错误用法示例**: ```csharp // 错误!会阻塞线程 var result = SomeAsyncMethod().Result; // 正确用法 var result = await SomeAsyncMethod(); ``` 通过这种机制,`await`在保持代码可读性的同时,实现了高效的异步编程模型。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值