协程觉醒:从同步世界到异步宇宙的跃迁
开篇:那个改变我编程思维的午后
三年前的一个下午,我盯着生产环境的监控面板,服务器 CPU 使用率只有 15%,但接口响应时间却飙到了 8 秒。那是一个典型的 I/O 密集型场景:系统需要同时调用 20 个第三方 API,每个接口平均耗时 400ms。
当时的代码是这样的:
importrequestsimporttime=time.time()foruser_idinuser_ids:response=requests.get(f'https://api.example.com/users/{user_id}')results.append(response.json())print(f"耗时:{time.time()-start:.2f}秒")returnresults# 获取 20 个用户数据fetch_user_data(range(1,21))# 输出: 耗时: 8.23秒CPU 闲着,线程却在无聊地等待网络响应。那一刻我意识到:我需要协程,一种让程序"主动放弃控制权"的魔法。
根据 Python 软件基金会 2024 年的调查,58% 的 Python 开发者已经在生产环境使用 asyncio,而在 Web 服务和数据处理领域,这个比例高达 78%。今天,让我们深入协程的本质,理解为什么它被称为"现代 Python 的并发基石"。
一、从操作系统说起:并发的三种武器
1.1 多进程:重装坦克
frommultiprocessingimportProcessimportosdefworker(name):print(f"进程{name}(PID:{os.getpid()}) 正在工作")if__name__=='__main__':processes=[Process(target=worker,args=(f'Worker-{i}',))foriinrange(3)]forpinprocesses:p.start()forpinprocesses:p.join()# 输出:# 进程 Worker-0 (PID: 12345) 正在工作# 进程 Worker-1 (PID: 12346) 正在工作# 进程 Worker-2 (PID: 12347) 正在工作优点:真正的并行,绕过 GIL
缺点:内存开销大(每个进程 ~10MB),进程间通信复杂
1.2 多线程:轻骑兵
importthreadingimporttimedefio_task(name):print(f"线程{name}开始")time.sleep(2)# 模拟 I/O 操作print(f"线程{name}完成")threads=[threading.Thread(target=io_task,args=(f'Thread-{i}',))foriinrange(3)]start=time.time()fortinthreads:t.start()fortinthreads:t.join()print(f"总耗时:{time.time()-start:.2f}秒")# 约 2 秒优点:共享内存,适合 I/O 密集型
缺点:受 GIL 限制,CPU 密集型无效,存在竞态条件
1.3 协程:魔法师
importasyncioasyncdefio_task(name):print(f"协程{name}开始")awaitasyncio.sleep(2)# 非阻塞等待print(f"协程{name}完成")returnnameasyncdefmain():start=time.time()tasks=[io_task(f'Coro-{i}')foriinrange(3)]results=awaitasyncio.gather(*tasks)print(f"总耗时:{time.time()-start:.2f}秒")# 约 2 秒returnresults asyncio.run(main(