程序的执行有“同步”和“异步”两种方式。 同步是一行干完再干下一行,异步是“你先去干,我接着走,好了叫我”。 同步就像你去柜台点餐,站在那儿等,菜出来了你才走,后面所有人都排队等你。 异步就像你扫码点餐,点完找个位置坐下玩手机,服务员做好了叫你,中间这段时间你可以干别的事。 代码里也一样。同步遇到耗时操作(读文件、查数据库、请求网络),程序就卡住不动,直到完成。异步遇到同样的事,把活丢出去,自己继续往下跑,等干完了再回来处理结果。 异步的核心是两个关键字——async 和 await。 async 标记方法“这里有异步操作” await 告诉程序“到这儿让出线程,先去干别的,等完了再回来继续”。 写法规则: 方法前加 async 返回类型改 Task<T>(有返回值)或 Task(无返回值) 耗时操作前加 await 异步方法命名约定,末尾加 Async。 比如 GetUser 写成 GetUserAsync,SaveData 写成 SaveDataAsync。 属于规定的写法。不这样写不会有语法问题,但是别人读起来会很有问题。 异步会“传染”,一个方法里 await 了,调用它的方法也得加 async。 从底层一路往上传,不能断。 // 底层用 await,变 async public async Task<string> ReadAsync() => await File.ReadAllTextAsync("test.txt"); // 上层调它,也得 await,也变 async public async Task ProcessAsync() { var content = await ReadAsync(); } 出现 await,所在方法必加 async,调用方也得跟着 await,一直传到入口。 拿异步结果用 await,尽量别用 .Result 或 .Wait()。 //一般是这样写 var data = await GetDataAsync(); //不建议这样写 var data = GetDataAsync().Result; 因为 await 和 .Result 的等待方式不同。 await 的等待:发现任务没完成,就把当前线程释放出去,让线程去干别的事。等任务完成了,再找个线程回来继续跑后面的代码。整个过程线程不闲着,也不死等。 .Result 的等待:直接卡住当前线程,什么都干不了,就硬等着。把异步当同步等。 这种等待会出现一个问题,死锁 什么叫死锁: 1. 线程甲(比如UI线程)发起一个异步任务(比如读文件)。 2. 然后它执行 .Result,死等这个任务完成。 3. 文件读完了,任务的“活儿”干完了。但是!默认情况下它必须返回线程甲去“交结果”。 4. 线程甲还在死等,无法去接收这个“交结果”的请求。 5. 于是,任务永远无法完成“交结果”这个最后动作。 6. 任务没完成,线程甲就继续等。死锁。 直接点说,用.Result等的任务不能是异步任务,否则会卡在最后提交不上结果导致死锁。 异步真正的价值不是让单个任务更快,而是让系统更“能扛”。 一个任务该花多长时间还是多长时间,但 1000 个任务同时涌进来,同步写法要 1000 个线程排队等,内存很快撑爆。异步只用几个线程,大部分任务在等待时不占线程,系统吞吐量高得多。 就像餐厅:同步是每个顾客配一个服务员全程站着等,100 个顾客需要 100 个服务员。异步是服务员点完就去服务下一桌,菜好了再去端,5 个服务员能服务 100 桌。 日常写异步就三步——方法标 async,返回包 Task,调用加 await。 无返回值的异步 public async Task DoAsync() 有返回值的异步 public async Task<string> GetAsync() 调用异步方法 var x = await GetAsync(); 知识点说完了,做做练习检验成果 下面这段代码有什么问题? public string GetData() { var result = File.ReadAllTextAsync("test.txt").Result; return result; } 答案: 用 .Result 死等异步结果,容易死锁。 没有 async 关键字。 正常项目没有Result的事,全部用await “给方法加上 async 关键字之后,这个方法就会自动在新线程上运行。” 这句话对吗?为什么? 答案: 不对 async 就是个标记,告诉编译器“这个方法里面会用到 await”。它本身不创建线程,更不保证代码跑在新线程上。 异步与否不取决于 async,而取决于 await 等的那个 Task 是否真正异步(比如真正的文件IO、网络请求)。如果你 await 的是一个已完成的任务,整个方法同步跑完,根本不切换线程。 补全代码,把下面同步方法改成异步版本: public List<string> ReadAllLines(string path) { return File.ReadAllLines(path).ToList(); } 答案 public async Task<List<string>>ReadAllLinesAsync(string path) { var lines = await File.ReadAllLinesAsync(path); return lines.ToList(); }
(一)基础篇
以下为完整正文内容。
正文
搜索结果
请输入关键词开始搜索。