Python异步编程深度解析:从生成器到协程
异步编程是Python进阶路上的一道分水岭。很多人会用async/await但遇到复杂场景就不知道怎么办了。这篇文章从底层原理讲起帮你真正理解Python的异步编程。一、从同步到异步为什么需要异步先看一个典型的同步代码pythonimport time import requests def fetch_data(url): print(f开始请求: {url}) response requests.get(url) print(f请求完成: {url}) return response.json() def main(): urls [ https://api.example.com/user/1, https://api.example.com/user/2, https://api.example.com/user/3, ] start time.time() results [fetch_data(url) for url in urls] print(f总耗时: {time.time() - start:.2f}秒) # 如果每个请求耗时1秒总耗时约3秒同步代码的问题是程序在等待网络响应时CPU处于空闲状态。如果能利用这些等待时间去干别的事效率就能大幅提升。异步编程的核心思想就是遇到IO等待时让出CPU给其他任务执行。二、生成器异步的基础在讲协程之前必须先理解生成器。生成器是Python实现协程的基础。生成器的基本用法pythondef simple_generator(): print(开始执行) yield 1 print(继续执行) yield 2 print(结束执行) yield 3 gen simple_generator() print(next(gen)) # 输出: 开始执行 / 1 print(next(gen)) # 输出: 继续执行 / 2 print(next(gen)) # 输出: 结束执行 / 3 # print(next(gen)) # StopIteration生成器的核心特性使用yield暂停函数执行调用next()恢复执行函数的状态局部变量、程序计数器会被保留生成器可以接收数据pythondef echo_generator(): while True: received yield print(f收到: {received}) gen echo_generator() next(gen) # 启动生成器执行到yield gen.send(hello) # 发送数据 gen.send(world)send()方法可以向生成器发送数据这让生成器既可以产出数据也可以接收数据。这就是协程的雏形。从生成器到协程的演变python# Python 3.3 引入 yield from def sub_generator(): yield 1 yield 2 yield 3 def main_generator(): # yield from 会代理子生成器 yield from sub_generator() yield 4 for value in main_generator(): print(value) # 1, 2, 3, 4yield from让生成器可以委托给另一个生成器这为协程的嵌套调用奠定了基础。三、asyncio事件循环驱动一切asyncio是Python异步编程的核心库。它的工作原理可以简单理解为有一个事件循环Event Loop任务协程被提交到事件循环事件循环不断轮询执行就绪的任务遇到await等待时任务让出控制权等待的事件完成后任务被重新调度一个简单的事件循环实现为了理解原理我们手写一个简化版的事件循环pythonfrom collections import deque from types import GeneratorType class SimpleEventLoop: def __init__(self): self.tasks deque() def create_task(self, coro): 将协程加入任务队列 self.tasks.append(coro) def run_until_complete(self): 运行事件循环直到所有任务完成 while self.tasks: task self.tasks.popleft() try: # 驱动协程执行 result next(task) # 如果协程返回的是迭代器继续执行 if isinstance(result, GeneratorType): self.tasks.append(result) except StopIteration: pass def task(name, n): 一个简单的协程任务 for i in range(n): print(f{name}: {i}) yield # 让出控制权 # 使用 loop SimpleEventLoop() loop.create_task(task(任务A, 3)) loop.create_task(task(任务B, 2)) loop.run_until_complete() # 输出: 任务A: 0, 任务B: 0, 任务A: 1, 任务B: 1, 任务A: 2这个简化版展示了事件循环的核心机制任务交替执行遇到yield就切换。四、async/await语法糖背后的本质Python 3.5正式引入async/await语法它们本质上是生成器的语法糖。python# 使用async定义协程 async def hello(): return Hello # 调用协程不会执行返回一个协程对象 coro hello() print(coro) # coroutine object hello at 0x... # 需要在事件循环中运行 import asyncio result asyncio.run(hello()) print(result) # Helloawait相当于yield from的升级版pythonasync def fetch_data(): await asyncio.sleep(1) # 模拟IO等待 return data async def main(): # await会等待协程完成 result await fetch_data() print(result)实际执行流程await告诉事件循环我要等待一个操作完成当前协程让出控制权事件循环去执行其他任务等待的操作完成后事件循环恢复这个协程五、创建和运行异步任务基础用法pythonimport asyncio import time async def task(name, delay): print(f{name}: 开始) await asyncio.sleep(delay) print(f{name}: 完成) return f{name}的结果 async def main(): # 方式1await逐个执行串行 start time.time() result1 await task(任务1, 2) result2 await task(任务2, 1) print(f串行耗时: {time.time() - start:.2f}秒) # 方式2并发执行 start time.time() # 创建任务协程自动调度 t1 asyncio.create_task(task(任务3, 2)) t2 asyncio.create_task(task(任务4, 1)) # 等待所有任务完成 results await asyncio.gather(t1, t2) print(f并发耗时: {time.time() - start:.2f}秒) asyncio.run(main())并发控制限制同时执行的数量pythonimport asyncio import aiohttp async def fetch_url(session, url, semaphore): async with semaphore: # 限制并发数 async with session.get(url) as response: return await response.text() async def crawl_many(urls, max_concurrent10): semaphore asyncio.Semaphore(max_concurrent) async with aiohttp.ClientSession() as session: tasks [ fetch_url(session, url, semaphore) for url in urls ] results await asyncio.gather(*tasks) return results # 爬取100个页面同时最多10个请求 urls [fhttp://example.com/page/{i} for i in range(100)] results asyncio.run(crawl_many(urls, 10))超时控制pythonimport asyncio async def slow_operation(): await asyncio.sleep(10) return 完成 async def main(): try: # 方式1使用timeout result await asyncio.wait_for(slow_operation(), timeout5) except asyncio.TimeoutError: print(操作超时) # 方式2使用timeout上下文管理器 try: async with asyncio.timeout(5): result await slow_operation() except TimeoutError: print(操作超时) asyncio.run(main())六、异步上下文管理器和异步迭代器异步上下文管理器pythonimport asyncio import aiohttp class AsyncResource: async def __aenter__(self): print(获取资源) await asyncio.sleep(1) return self async def __aexit__(self, exc_type, exc_val, exc_tb): print(释放资源) await asyncio.sleep(1) async def use_resource(): async with AsyncResource() as resource: print(使用资源) asyncio.run(use_resource())python# 使用contextlib简化 from contextlib import asynccontextmanager asynccontextmanager async def get_db_connection(): conn await create_connection() try: yield conn finally: await conn.close() async def query_data(): async with get_db_connection() as conn: return await conn.execute(SELECT * FROM users)异步迭代器pythonimport asyncio class AsyncCounter: def __init__(self, max_count): self.max_count max_count self.count 0 def __aiter__(self): return self async def __anext__(self): self.count 1 if self.count self.max_count: raise StopAsyncIteration await asyncio.sleep(0.1) return self.count async def main(): async for num in AsyncCounter(10): print(num) asyncio.run(main())python# 使用async生成器Python 3.6 async def async_counter(max_count): for i in range(1, max_count 1): await asyncio.sleep(0.1) yield i async def main(): async for num in async_counter(10): print(num) asyncio.run(main())七、异步中的并发原语锁Lockpythonimport asyncio class Counter: def __init__(self): self.value 0 self.lock asyncio.Lock() async def increment(self): async with self.lock: # 临界区 current self.value await asyncio.sleep(0.01) # 模拟IO self.value current 1 async def worker(counter, n): for _ in range(n): await counter.increment() async def main(): counter Counter() # 创建10个并发任务每个增加100次 tasks [worker(counter, 100) for _ in range(10)] await asyncio.gather(*tasks) print(f最终值: {counter.value}) # 应该是1000 asyncio.run(main())事件Eventpythonimport asyncio async def waiter(event): print(等待事件...) await event.wait() print(事件已触发继续执行) async def setter(event): await asyncio.sleep(2) print(触发事件) event.set() async def main(): event asyncio.Event() await asyncio.gather( waiter(event), setter(event) ) asyncio.run(main())队列Queuepythonimport asyncio import random async def producer(queue, name): for i in range(5): item f{name}-{i} await queue.put(item) print(f生产者{name} 生产: {item}) await asyncio.sleep(random.random()) await queue.put(None) # 结束信号 async def consumer(queue, name): while True: item await queue.get() if item is None: break print(f消费者{name} 消费: {item}) await asyncio.sleep(random.random() * 0.5) queue.task_done() async def main(): queue asyncio.Queue(maxsize10) producers [producer(queue, fP{i}) for i in range(2)] consumers [consumer(queue, fC{i}) for i in range(3)] await asyncio.gather( *producers, *consumers ) asyncio.run(main())八、异步中的常见陷阱陷阱1忘记awaitpython# ❌ 错误 async def get_data(): return data async def main(): result get_data() # 没有awaitresult是一个协程对象 print(result) # coroutine object get_data at 0x... # ✅ 正确 result await get_data()陷阱2阻塞操作python# ❌ 错误使用阻塞的time.sleep async def bad_example(): time.sleep(1) # 会阻塞整个事件循环 # ✅ 正确使用异步版本 async def good_example(): await asyncio.sleep(1) # ❌ 错误使用阻塞的requests async def bad_request(): response requests.get(http://example.com) # 阻塞 # ✅ 正确使用aiohttp async def good_request(): async with aiohttp.ClientSession() as session: async with session.get(http://example.com) as resp: return await resp.text()陷阱3创建任务但未等待python# ❌ 问题创建了任务但没有等待 async def main(): asyncio.create_task(long_task()) # 任务可能还没执行完就结束了 print(主函数结束) # ✅ 解决等待所有任务完成 async def main(): task asyncio.create_task(long_task()) await task # 等待任务完成 # 或者使用asyncio.gather # await asyncio.gather(task)陷阱4在同步函数中调用异步函数python# ❌ 错误不能在同步函数中用await def sync_function(): result await async_function() # SyntaxError # ✅ 解决1将整个函数改为异步 async def async_wrapper(): return await async_function() # ✅ 解决2使用asyncio.run() def sync_function(): result asyncio.run(async_function()) return result九、实战异步爬虫下面是一个完整的异步爬虫示例综合运用了上述知识pythonimport asyncio import aiohttp from aiohttp import ClientTimeout, TCPConnector import json from typing import List, Dict import logging logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) class AsyncCrawler: def __init__(self, max_concurrent20, timeout30): self.max_concurrent max_concurrent self.timeout ClientTimeout(totaltimeout) self.semaphore asyncio.Semaphore(max_concurrent) self.session None async def __aenter__(self): connector TCPConnector(limitself.max_concurrent) self.session aiohttp.ClientSession( connectorconnector, timeoutself.timeout, headers{ User-Agent: Mozilla/5.0 (compatible; AsyncCrawler/1.0) } ) return self async def __aexit__(self, *args): await self.session.close() async def fetch(self, url: str, retry: int 3) - Dict: 抓取单个URL支持重试 for attempt in range(retry): try: async with self.semaphore: logger.info(f抓取: {url}) async with self.session.get(url) as response: if response.status ! 200: raise Exception(f状态码: {response.status}) # 根据Content-Type决定解析方式 content_type response.headers.get(Content-Type, ) if json in content_type: return await response.json() else: return {content: await response.text(), url: url} except asyncio.TimeoutError: logger.warning(f{url} 超时重试 {attempt1}/{retry}) except Exception as e: logger.warning(f{url} 错误: {e}重试 {attempt1}/{retry}) await asyncio.sleep(1) return {error: True, url: url} async def crawl(self, urls: List[str]) - List[Dict]: 批量抓取 tasks [self.fetch(url) for url in urls] results await asyncio.gather(*tasks, return_exceptionsTrue) # 处理异常结果 final_results [] for r in results: if isinstance(r, Exception): final_results.append({error: True, exception: str(r)}) else: final_results.append(r) return final_results async def main(): urls [ https://api.github.com/users/octocat, https://api.github.com/users/defunkt, https://api.github.com/users/github, https://httpbin.org/delay/2, # 故意延迟 https://httpbin.org/status/404, # 会出错 ] async with AsyncCrawler(max_concurrent3, timeout10) as crawler: results await crawler.crawl(urls) # 统计结果 success [r for r in results if not r.get(error)] failed [r for r in results if r.get(error)] print(f成功: {len(success)}失败: {len(failed)}) for result in results: if result.get(error): print(f❌ {result.get(url, unknown)}: {result.get(exception, unknown error)}) else: print(f✅ {result.get(url, unknown)}) if __name__ __main__: asyncio.run(main())十、异步性能对比来一个直观的对比pythonimport time import asyncio import requests import aiohttp # 同步版本 def sync_demo(): urls [https://httpbin.org/delay/1] * 10 start time.time() for url in urls: requests.get(url) return time.time() - start # 异步版本 async def async_demo(): urls [https://httpbin.org/delay/1] * 10 start time.time() async with aiohttp.ClientSession() as session: tasks [session.get(url) for url in urls] responses await asyncio.gather(*tasks) for resp in responses: await resp.text() return time.time() - start # 运行对比 print(f同步耗时: {sync_demo():.2f}秒) # 约10秒 print(f异步耗时: {asyncio.run(async_demo()):.2f}秒) # 约1秒写在最后异步编程是Python性能优化的利器但也是需要认真学习的知识点。学习建议先理解事件循环的概念从简单的asyncio.sleep()开始试验逐步引入网络请求、数据库操作遇到问题多调试理解协程的生命周期阅读优秀框架的源码如FastAPI、aiohttp记住异步不是万能药。对于CPU密集型任务异步帮不上忙对于简单的IO操作异步可能带来不必要的复杂度。选择合适的工具解决合适的问题。

相关新闻

生化危机4重制版修改器下载及其用法解析

生化危机4重制版修改器下载及其用法解析

下载链接 《生化危机4:重制版》风灵月影修改器:三十六项全功能解析与REFramework前置 《生化危机4:重制版》采用Capcom RE引擎开发,在保留原作精华的基础上重构了战斗机制、经济系统和资源管理逻辑。风灵月影(FLiNG&a…

2026/7/24 23:36:24 阅读更多 →
【Logisim】4-2,8-3与16-4编码器的详细教程

【Logisim】4-2,8-3与16-4编码器的详细教程

1. 引言在数字逻辑电路设计中,编码器(Encoder)是一种将多个输入信号为更少输出线(二进制编码)的组合逻辑电路。它是多路复用器、键盘编码等应用的核心组件。在真实的芯片(如 74LS148)中&#xf…

2026/7/24 23:36:24 阅读更多 →
赛博朋克2077修改器下载及其用法

赛博朋克2077修改器下载及其用法

下载链接 《赛博朋克2077》风灵月影修改器:从v1.03到v2.13的技术演进 《赛博朋克2077》作为CD Projekt RED开发的开放世界RPG,其复杂的人物培养、义体改造和经济系统为修改器提供了丰富的功能切入点。风灵月影(FLiNG)制作的修改…

2026/7/24 23:36:24 阅读更多 →

最新新闻

YOLOv8在水稻病害智能检测中的实践与优化

YOLOv8在水稻病害智能检测中的实践与优化

1. 项目背景与核心价值水稻作为全球主要粮食作物,其病害防控直接影响粮食安全。传统病害识别依赖农技人员目测,存在效率低、主观性强等问题。本项目采用YOLOv8构建的智能检测系统,可实现田间病害的实时自动化识别,平均识别速度达到…

2026/7/24 23:42:27 阅读更多 →
电商AI Agent架构设计与智能导购系统实现

电商AI Agent架构设计与智能导购系统实现

1. 电商场景下的AI Agent技术架构解析在电商领域部署AI Agent系统时,我们通常采用分层架构设计。最底层是数据接入层,通过API网关整合商品数据库、用户行为日志、交易系统等数据源。中间层是核心AI引擎,包含自然语言处理模块(处理…

2026/7/24 23:42:27 阅读更多 →
千笔智能体:AIGC降噪技术提升内容自然度

千笔智能体:AIGC降噪技术提升内容自然度

1. 项目概述:千笔专业降AI率智能体的核心定位作为一款定位"断层领先"的AIGC降噪工具,千笔智能体在内容创作领域解决了一个关键痛点:如何让AI生成内容更接近人类创作的自然感。我在实际测试中发现,当前市面上大多数AI写作…

2026/7/24 23:42:27 阅读更多 →
Excel/WPS自动化排班表制作:函数+条件格式+数据有效性实战

Excel/WPS自动化排班表制作:函数+条件格式+数据有效性实战

今天我们来解决一个实际问题:如何用Excel或WPS制作自动化的员工排班表。无论是企业HR、部门主管还是小店老板,只要涉及多人轮班,手工排班既耗时又容易出错。通过函数公式、条件格式和数据有效性的组合,我们可以实现排班表的半自动…

2026/7/24 23:42:27 阅读更多 →
AI驱动的CAE仿真智能体技术解析与应用

AI驱动的CAE仿真智能体技术解析与应用

1. CAE仿真智能体的技术革命CAE(计算机辅助工程)仿真领域正在经历一场由AI驱动的范式转移。传统仿真流程中,工程师需要手动设置边界条件、划分网格、选择求解器参数并分析结果,整个过程耗时且依赖经验。而仿真智能体的出现&#x…

2026/7/24 23:42:27 阅读更多 →
2026知名的全球EMBA中立择校测评

2026知名的全球EMBA中立择校测评

民营企业家、企业创始人择校EMBA,普遍面临排名杂乱、课程同质化、圈层适配模糊、资源落地性不明等问题。本文从全球办学排名、院校办学定位、课程体系、学员圈层、产业资源五大维度,对知名的全球EMBA主流项目做客观横向对比,无商业推广、无夸…

2026/7/24 23:41:27 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻