00. 目录文章目录00. 目录01. 什么是协程02. 协程函数和协程对象03. await关键字04. 多个任务同步执行05. 多个任务异步执行06. asyncio.gather07. 下载图片案例08. 附录01. 什么是协程**概念**协程Coroutine是一种线程内部的任务调度机制它通过事件循环在用户态中实现任务的挂起与恢复执行从而在遇到 IO 操作时不让 CPU 等待而是继续执行其它需要 CPU 的任务。协程的本质就是在一个线程里趁着某些任务在等 IO把 CPU 交给其它任务去用。关键点1️⃣协程不是线程也不是进程协程不是操作系统提供的并且 CPU 看不见协程。操作系统不知道协程的存在。协程是程序员在用户态用代码“设计出来”的任务切换机制。关键点2️⃣协程发生在一个线程内部协程不是线程之间的切换。而是线程内部多个任务之间的切换。本质是一个线程里写了很多任务由事件循环统一调度。关键点3️⃣协程的核心能力挂起与恢复当任务 遇到 IO 操作 时任务会被挂起。当 IO 操作完成后任务会被恢复执行。关键点4️⃣协程依赖一个关键角色事件循环事件循环负责调度任务、判断是否该挂起、决定何时恢复执行事件循环是协程系统的“大脑”关键点5️⃣协程的目标是尽量减少线程切换在单线程场景下最大化 CPU 利用率特别适合 IO 密集型任务02. 协程函数和协程对象协程函数(coroutine Function)使用『async关键字』修饰的函数就是协程函数。协程对象(coroutine Object)调用『协程函数』就会得到『协程对象』。注意调用『协程函数』并不会执行『协程函数』中的代码。# 定义一个协程函数asyncdefwork():print(work开始)print(work执行中......)print(work结束)return工作结果# 调用协程函数会得到协程对象coroutine_objectwork()将协程对象交给asyncio.run()asyncio.run()会将协程对象包装成一个任务交给事件循环。# asyncio.run 方法做了3件事# 1.创建一个事件循环。# 2.将收到的协程对象包装成一个任务task交给事件循环。# 3.启动事件循环。# 注意asyncio.run 会阻塞当前线程直到任务执行完毕并返回该任务 return 的最终结果。resultasyncio.run(coroutine_object)print(result)03. await关键字await 关键有三个作用**挂起**await 会暂停当前协程的执行。**等待**遇到 await 关键字事件循环会立即安排 await 后面的对象去执行并等待该对象执行完成并且可以拿到执行结果。**关键点**在执行 await 后面的对象时会出现两种情况:情况一如果在执行该对象中的代码时遇到了【await I/O操作】需要等待外部资源返回结果的操作例如网络请求、文件读写等那 CPU 的控制权就会交给事件循环。事件循环会去调度循环中的其他任务如果有的话。**情况二**如果该对象中的代码不包含任何【await I/O操作】。例如print打印、数学计算、逻辑计算等。此时事件循环拿不到 CPU 控制权无法调度循环中的其他任务不会发生任务切换。**恢复**当 await 后的对象执行完毕事件循环会恢复之前被挂起的协程该协程会从当时挂起的位置继续执行并拿到返回值。注意await 后面只能写『可等待对象』常见可等待对象有协程对象、Future对象、Task对象。importasyncioasyncdefwork():print(work开始)print(work执行中......)# await去等待一个协程对象靠asyncio.sleep方法返回一个协程对象resawaitasyncio.sleep(2)print(res)print(work结束)return工作结果asyncdefmain():print(main开始)# await去等待一个协程对象靠自己去编写协程函数随后调用该函数来得到协程对象resawaitwork()print(res)print(main结束)returnmain的返回值resultasyncio.run(main())print(result)04. 多个任务同步执行使用 await 实现多个任务同步执行importasyncioimporttime# 定义一个协程函数asyncdefwork(n,delay):print(fwork{n}开始)print(fwork{n}执行中......)# 模拟一个IO等待awaitasyncio.sleep(delay)print(fwork{n}结束)returnfwork{n}的返回值asyncdefmain():print(main开始)starttime.time()# 调用三次work函数分别得到三个协程对象coroutine1work(1,2)coroutine2work(2,2)coroutine3work(3,2)# 此处会等待coroutine1执行完成res1awaitcoroutine1print(res1)# 等待上面的coroutine1完成后再等待coroutine2完成res2awaitcoroutine2print(res2)# 等待上面的coroutine2完成后再等待coroutine3完成res3awaitcoroutine3print(res3)print(main结束,time.time()-start)return我是main的返回值# 将协程对象交给事件循环resultasyncio.run(main())print(result)05. 多个任务异步执行使用asyncio.create_task()方法向事件循环中添加任务从而实现多个任务异步执行。importasyncioimporttime# 定义一个协程函数asyncdefwork(n,delay):print(fwork{n}开始)print(fwork{n}执行中......)# 模拟一个IO等待awaitasyncio.sleep(delay)print(fwork{n}结束)returnfwork{n}的返回值asyncdefmain():print(main开始)starttime.time()# asyncio.create_task 会把一个协程对象包装成一个可被事件循环调度的任务并注册到事件循环中task1asyncio.create_task(work(1,2))task2asyncio.create_task(work(2,2))task3asyncio.create_task(work(3,2))# 此处会等待task1执行完成res1awaittask1print(res1)# 等待上面的task1完成后再等待task2完成res2awaittask2print(res2)# 等待上面的task2完成后再等待task3完成res3awaittask3print(res3)print(main结束,time.time()-start)return我是main的返回值# 将协程对象交给事件循环resultasyncio.run(main())print(result)06. asyncio.gatherasyncio.gather方法可以把多个协程对象丢给事件循环并在全部执行完后一次性拿到所有结果。importasyncioimporttime# 定义一个协程函数asyncdefwork(n,delay):print(fwork{n}开始)print(fwork{n}执行中......)# 模拟一个IO等待awaitasyncio.sleep(delay)print(fwork{n}结束)returnfwork{n}的返回值asyncdefmain():print(main开始)starttime.time()# 把多个协程对象同时丢给事件循环并在全部执行完后一次性拿到所有结果。resultawaitasyncio.gather(work(1,2),work(2,2),work(3,2))print(result)print(main结束,time.time()-start)return我是main的返回值# 将协程对象交给事件循环resultasyncio.run(main())print(result)07. 下载图片案例1️⃣使用传统方式下载图片如下代码的特点图片是一张一张下载的当前图片没有下载完成后一张图片的下载就不能开始这属于典型的同步下载。importrequestsdefdownload_picture(url):print(f开始下载{url})# 发送网络请求获取这张图片responserequests.get(url)print(下载完毕)# 保存图片到本地withopen(url[-10:],wb)asfile:file.write(response.content)defmain():url_list[https://n.sinaimg.cn/spider20260129/217/w600h417/20260129/3e26-917ee55a8a42b8626807c332c24981de.png,https://n.sinaimg.cn/finance/transform/97/w630h267/20260129/97c4-b211cc51784830f09ee19e450475c93b.png,https://n.sinaimg.cn/spider20260129/539/w1439h700/20260129/e09a-cc2ca319e00f701ccfca3ebc62aa8772.png]forurlinurl_list:download_picture(url)main()2️⃣使用协程方式下载图如下代码的特点多张图片会几乎同时发起下载请求当某一张图片在等待网络数据返回时其它图片的下载任务并不会被阻塞而是可以继续执行这属于典型的协程并发下载。importaiohttpimportasyncioasyncdefdownload_picture(session,url):print(f开始下载{url})# 发送网络请求获取这张图片请求发出去后要等待服务器把数据返回等的这段时间就是IO等待responseawaitsession.get(url)# 等待数据图片数据可能分多次传输需要等待数据全部读完等的这段时间也是IO等待contentawaitresponse.read()print(下载完毕)# 保存图片到本地withopen(url[-10:],wb)asfile:file.write(content)# 释放连接资源告诉 aiohttp这个连接我不用了你可以回收了awaitresponse.release()asyncdefmain():url_list[https://n.sinaimg.cn/spider20260129/217/w600h417/20260129/3e26-917ee55a8a42b8626807c332c24981de.png,https://n.sinaimg.cn/finance/transform/97/w630h267/20260129/97c4-b211cc51784830f09ee19e450475c93b.png,https://n.sinaimg.cn/spider20260129/539/w1439h700/20260129/e09a-cc2ca319e00f701ccfca3ebc62aa8772.png]# 创建会话对象发请求的工具sessionaiohttp.ClientSession()# 创建多个协程对象coroutine_list[download_picture(session,url)forurlinurl_list]# 将多个协程对象交给事件循环awaitasyncio.gather(*coroutine_list)# 关闭会话awaitsession.close()asyncio.run(main())08. 附录
阅读完成 · 觉得有帮助?