协程的核心概念协程是一种线程内部的任务调度机制通过事件循环实现任务的挂起与恢复执行。其核心目标是在单线程内高效处理多任务避免不必要的CPU等待。协程与线程的关系协程运行在线程内部并非线程间的切换。操作系统无法感知协程的存在它完全由程序员通过代码实现的任务切换机制控制。协程的设计初衷是减少线程切换的开销。事件循环的作用事件循环是协程的核心调度器主要承担三项职责维护任务队列监听I/O事件管理协程的挂起与恢复asyncio.run()是启动事件循环的标准入口点。I/O操作识别在协程中任何需要等待外部响应的操作都属于I/O操作。这些操作通常以await关键字为标识awaitasyncio.sleep(1)# 时间等待型I/Oawaitaiohttp.get(url)# 网络请求型I/Oawaitfile.read()# 磁盘读写型I/O性能优势创建子进程比创建子线程消耗更多CPU资源。协程通过以下方式优化性能单线程内任务切换无需上下文切换遇到I/O时立即释放CPU资源仅对I/O密集型任务有效实现原理当事件循环将任务交给CPU执行时若检测到await关键字会立即挂起当前任务。待I/O操作完成后事件循环会自动恢复该任务的执行。这种机制确保CPU资源始终被有效利用。协程函数与协程对象协程函数是通过async关键字修饰的函数调用协程函数会返回一个协程对象。调用协程函数本身不会执行函数内的代码而是生成一个待执行的协程对象。asyncdefwork():print(work开始工作)print(work正在工作.......)print(work作结束)return我工作结束了!!!协程对象的执行协程对象需要通过事件循环如asyncio.run()来执行。asyncio.run()会自动管理协程的执行流程包括调用send方法并返回结果。coroutine_objwork()asyncio.run(coroutine_obj)协程与线程的区别协程是单线程下的并发模型通过事件循环调度任务。同一时刻CPU只执行一个线程但协程可以在单个线程内实现任务切换避免线程切换的开销。关键点总结协程函数需用async定义调用后返回协程对象。协程对象需通过asyncio.run()或事件循环触发执行。协程在单线程内实现并发效率高于多线程。协程适合I/O密集型任务避免线程阻塞问题。协程基本概念协程是一种轻量级的线程由用户控制调度在单线程内实现并发。通过async/await语法实现能够高效处理 I/O 密集型任务。协程的状态包括PENDING刚创建尚未执行。SUSPENDED遇到await暂停执行。FINISHED执行完成或抛出异常。await关键字的作用await用于挂起当前协程的执行直到等待的对象完成挂起暂停当前协程将控制权交还给事件循环。等待事件循环执行await后的对象若对象不涉及 I/O事件循环无法切换其他任务。若对象涉及 I/O如网络请求、文件读写事件循环可调度其他任务。恢复对象执行完成后事件循环恢复挂起的协程继续执行并返回结果。I/O 操作与协程的关系I/O 操作程序与外部设备如磁盘、网络交换数据会导致 CPU 等待。协程优化通过await标记 I/O 操作点事件循环可在等待期间切换其他协程避免 CPU 空转。代码示例分析importasyncioasyncdefstudy():print(开始学习)awaitasyncio.sleep(2)# 模拟 I/O 操作print(学习结束)return学习返回asyncdefmain():print(main开始)awaitstudy()# 等待 study() 协程完成print(main结束)returnmain返回resasyncio.run(main())# 运行事件循环print(res)执行流程asyncio.run(main())启动事件循环执行main()协程。main()打印main开始随后await study()挂起main()执行study()。study()打印开始学习await asyncio.sleep(2)挂起study()事件循环等待 2 秒。study()恢复后打印学习结束返回学习返回。main()恢复后打印main结束返回main返回。最终输出main返回。关键注意事项await后仅接受可等待对象协程对象、Task、Future或实现了__await__方法的对象。事件循环的调度依赖于 I/O 操作。纯计算任务无await会阻塞事件循环。asyncio.run()是高级接口负责创建事件循环并运行协程。异步编程核心概念asyncio是 Python 实现异步编程的标准库基于协程Coroutine和事件循环Event Loop构建。以下代码演示了关键机制asyncdefcoro():# 协程函数定义awaitasync_operation()# 挂起执行点任务调度原理asyncio.create_task()将协程包装为 Task 对象并立即加入事件循环。多个任务通过await显式等待时事件循环会自动调度task1asyncio.create_task(work(1,2))# 立即开始执行task2asyncio.create_task(work(2,2))# 并发执行执行流程控制await表达式实现非阻塞等待当遇到 IO 操作时自动切换上下文。同步代码与异步代码的混合执行需注意print(同步输出)# 立即执行awaitasyncio.sleep(1)# 挂起当前协程返回值处理机制协程返回值通过await获取asyncio.run()返回主协程的最终结果resultawaittask# 获取单个任务返回值final_resasyncio.run(main())# 获取入口函数返回值性能测量方法使用time.time()计算异步操作的耗时需注意测量点的位置starttime.time()awaittask1# 包含在此范围内的await都会计入耗时print(time.time()-start)异步编程核心概念asyncio是 Python 的异步 I/O 框架基于事件循环实现协程并发。示例代码展示了以下关键点协程定义通过async def声明协程函数如work()和main()。可等待对象await后接协程、Task 或 Future 对象例如await asyncio.sleep(delay)。事件循环asyncio.run(main())启动事件循环并执行顶层协程。并发执行机制asyncio.gather()用于并发运行多个协程resawaitasyncio.gather(work(n1,delay8),work(n2,delay4),work(n3,delay2))所有协程并行执行总耗时由最长延迟8秒决定。返回值按输入顺序返回列表。同步与异步对比注释部分展示了同步调用方式res1awaitwork(n1,delay2)# 需等待2秒res2awaitwork(n2,delay2)# 再等待2秒res3awaitwork(n3,delay2)# 又等待2秒同步调用总耗时为各任务延迟之和6秒。异步并发总耗时仅为最慢任务的延迟8秒。调试与性能分析代码通过time.time()记录执行时间starttime.time()# ...异步操作...print(main结束,time.time()-start)# 输出总耗时验证异步并发的效率优势。实际场景可用于性能基准测试。返回值处理协程返回值通过两种方式传递直接通过return返回如work()返回字符串。asyncio.run()的返回值是顶层协程的返回结果最终被打印。注意事项避免混用阻塞操作如time.sleep与异步代码。所有异步操作需在协程函数内通过await调用。事件循环由asyncio.run()自动管理通常无需手动创建。异步编程与同步下载的区别同步下载代码会阻塞当前线程必须等待当前图片完全下载完成后才能开始下一张图片的下载。这种模式在I/O密集型任务中效率较低因为大部分时间都浪费在等待网络响应上。异步下载利用事件循环和非阻塞I/O操作可以在等待一个下载完成的同时开始其他下载任务。这种模式特别适合处理大量网络请求能显著提高程序的吞吐量。异步编程关键知识点事件循环(Event Loop)异步程序的核心负责调度和执行协程任务通过asyncio.run()自动创建和管理可以同时监控多个I/O操作的状态协程(Coroutine)使用async def定义的异步函数通过await表达式暂停执行直到等待的操作完成协程对象需要被事件循环调度才能执行aiohttp库要点ClientSession是异步HTTP客户端的主要接口需要配合async with上下文管理器使用响应对象的read()方法也是异步操作需要await任务调度asyncio.gather()用于并发运行多个协程接收多个协程对象作为参数返回所有协程结果的聚合列表代码优化建议异常处理应该更加细致特别是网络请求可能出现的各种错误。文件名生成方式可以改进当前截取URL最后10个字符的方法不够可靠。可以考虑使用更完善的临时文件命名方案或保留原始文件名。性能对比异步版本在下载多张图片时优势明显同步版本是串行执行总时间各图片下载时间之和异步版本是并行执行总时间≈最慢的单个图片下载时间当图片数量增加时异步版本的优势会成倍放大代码实现fromidlelib.rpcimportresponse_queueimportrequestsdefdownload(url):print(f开始下载)responserequests.get(url)print(response.content)# 保存图片到本地withopen(url[-10:],wb)asf:f.write(response.content)print(f下载成功)defmain():url_list[https://pic4.zhimg.com/v2-ff9deaad173974d1f0dca26b8e879299_1440w.jpg,#https://ts2.tc.mm.bing.net/th/id/OIP-C.5AmeaglwRvHzzdbFVQcJ-QHaNK?rs1pidImgDetMaino7rm3,#https://tse4.mm.cn.bing.net/th/id/OIP-C.9QuK_KvGcFEJChiNU3nCywHaLH?w196h294c7r0o7dpr1.3pid1.7rm3,]forurlinurl_list:download(url)main()asyncdefdownload(session,url):print(f开始下载:{url})try:# 发送异步请求asyncwithsession.get(url)asresponse:ifresponse.status200:# 读取图片数据contentawaitresponse.read()# 取URL最后10个字符当文件名filenameurl[-10:]# 保存文件withopen(filename,wb)asf:f.write(content)print(f✅ 下载成功:{filename})else:print(f❌ 下载失败状态码:{response.status})exceptExceptionase:print(f⚠️ 下载出错:{e})asyncdefmain():# 要下载的图片URL列表url_list[https://pic4.zhimg.com/v2-ff9deaad173974d1f0dca26b8e879299_1440w.jpg,]# 创建异步会话asyncwithaiohttp.ClientSession()assession:# 创建协程任务列表coroutine_list[download(session,url)forurlinurl_list]# 并发执行所有任务awaitasyncio.gather(*coroutine_list)if__name____main__:asyncio.run(main())# 1. 创建一个事件循环# 2.将收到 的写成对象包装程一个任务交给时间循环# 启动时间循环