GIL:绕不开的那道墙
CPython解释器有一把全局锁,叫GIL。同一时刻,只有一个线程能执行Python字节码。这意味着多线程在CPU密集型任务上几乎无法并行,四个线程跑计算,总耗时可能和单线程差不多,甚至更慢。多线程的优势在I/O:当线程等待网络、磁盘、数据库时,GIL会被释放,其他线程趁机运行。所以多线程适合“等”多于“算”的场景。多进程则绕开了GIL,每个进程有独立的解释器和内存空间,能真正并行利用多核。异步则是单线程事件循环,靠协程切换,没有线程创建和上下文切换的开销。三者的分水岭,在于任务是在等,还是在算。
多线程:适合I/O密集,但别贪多
爬虫抓取网页、批量请求API、读写小文件,这些任务大部分时间在等待。多线程能让你在等一个响应时去发下一个请求,吞吐量成倍提升。但线程不是越多越好。线程切换有成本,共享内存还要加锁。锁用不好,死锁、竞态条件接踵而至。一个实用建议:I/O密集型任务,线程数可以设为CPU核数的几倍,但别动辄开几百个。用concurrent.futures.ThreadPoolExecutor管理线程池,比手动threading更稳。记住,多线程适合“等”,不适合“算”。
多进程:CPU密集的硬解法
图像处理、视频转码、科学计算、大规模数据清洗,这些任务CPU吃满。多进程让每个核心独立干活,性能随核数线性增长。multiprocessing或ProcessPoolExecutor是常用工具。但进程间不共享内存,数据传递要序列化,开销比线程大。进程启动也慢。所以任务粒度不能太细,否则通信成本吃掉并行收益。另外,Windows下多进程要放在if __name__ == '__main__':里,否则会无限递归创建进程。多进程适合“算”,但要注意数据交换的代价。
异步:高并发I/O的轻量武器
异步编程用async和await,在单线程内实现并发。协程切换由程序控制,没有线程切换的开销,内存占用极低。一个异步程序可以轻松维持上万并发连接。Web服务、API网关、实时通信、高频网络请求,异步是首选。但它要求整个调用链都是异步的,一个同步阻塞调用就能卡死事件循环。异步生态也有局限,很多库还不支持。异步适合“等”且“等得密集”的场景,代码复杂度比多线程高,调试也更难。用得好是利器,用不好是迷宫。
怎么选:看任务,也看生态
判断标准可以浓缩成一句话:CPU密集用多进程,I/O密集用多线程或异步,高并发I/O优先异步。如果任务混合了计算和I/O,可以组合使用:异步处理网络请求,把计算结果丢给进程池。Web框架里,FastAPI用异步,Django的ORM多数同步,选择要跟着生态走。没有银弹,只有权衡。先分析瓶颈在哪里:是CPU跑满,还是等待太长?用cProfile、py-spy定位,再决定用哪种并发模型。别为了并发而并发,简单场景同步代码反而最可靠。选对了工具,Python的并发能力远超你想象。