Python 并发编程实战:线程、进程与协程的选择策略
关于并发编程存在三条可选途径, 其一为多线程形式, 其二属多进程方式, 其三是异步协程模式。表面上看这三种方式似乎均可实现同时进行多项事务的效果, 然而它们各自所适用的具体场景的差异程度极大。一旦选错了相应的模型, 在代码已经全部编写完成之后才察觉到性能不但没有提升反而出现下降的情况, 像这类案例在代码领域当中实在是太过常见了。这段文字起始于GIL, 进而逐个剖析三种并发模型的底层机制, 接着阐述其适用边界, 随后展示代码示例, 最终给出一张清晰明确的选型决策表。一、GIL绕不开的话题一个解释器的设计决策是GILLock即全局解释器锁, 在同一时刻, 仅有一个线程可执行字节码那这究竟意味着啥呢? 哪怕你开启了有八个线程, 让它们运行在具备八核的中央处理器之上, 然而代码的实行依旧是呈串行化进行的。多个线程在处于纯粹由中央处理器操作带动负担集中执行的任务之时, 并不能在速度上演进的节奏获得提升, 反而会更缓慢, 究其原因在于线程更迭转换带来的额外消耗所导致的。import threading import time def cpu_bound(n): total 0 for i in range(n): total i ** 2 return total start time.perf_counter cpu_bound(10_000_000) print(f单线程: {time.perf_counter - start:.3f}s) start time.perf_counter threads [threading.Thread(targetcpu_bound, args(2_500_000,)) for _ in range(4)] for t in threads: t.start for t in threads: t.join print(f4线程: {time.perf_counter - start:.3f}s) # 大概率比单线程更慢不过, GIL存在着一个关键的例外情况, 那就是, 当线程处于等待IO这种状态的时候, 这里所说的IO涵盖网络请求、磁盘读写以及数据库查询, 在这个时候, GIL是会被释放掉的, 然后其他的线程就能够继续去执行了。而这恰恰就是多线程在那种IO密集型场景之下依旧能够生效的最为根本的原因所在了。二、IO 密集场景的首选 2.1 基本用法import threading import requests from concurrent.futures import ThreadPoolExecutor urls [https://httpbin.org/delay/1] * 10 def fetch(url): resp requests.get(url, timeout10) return resp.status_code with ThreadPoolExecutor(max_workers5) as executor: results list(executor.map(fetch, urls)) print(f完成 {len(results)} 个请求)2.2 线程安全与锁多线程共享内存空间写操作必须加锁counter 0 lock threading.Lock def increment: global counter for _ in range(100_000): with lock: # 等同于 lock.acquire / lock.release counter 1看起来 1 好像是原子操作, 然而在字节码层面它却被分成了多条指令, 先是读取, 然后加 1, 最后写入。当不加锁的时候, 线程 A 会读到旧的值, 线程 B 同样也会读到旧的值, 之后它们各自进行加 1 操作并写回, 最终导致结果少算了一次。和queue.Queue不同的另一种选择是 , 它是线程安全的队列 , 这种队列出于自然的特性而适合生产者 - 消费者模式:from queue import Queue import threading def producer(q): for i in range(100): q.put(i) def consumer(q): while True: item q.get if item is None: break process(item)2.3 适用场景 三、CPU 密集场景的解法把进程进行更换从而绕开了GIL, 每一个进程具备着独立的, 作为独立存在的解释器以及占据各自固定范围的内存空间, 切实地凭借多核CPU去达成相关任务。from concurrent.futures import ProcessPoolExecutor import time def cpu_bound(n): total 0 for i in range(n): total i ** 2 return total start time.perf_counter with ProcessPoolExecutor(max_workers4) as executor: results list(executor.map(cpu_bound, [2_500_000] * 4)) print(f4进程: {time.perf_counter - start:.3f}s) # 接近线性加速代价也很明显3.1 进程间通信from multiprocessing import Process, Queue def worker(q): q.put({result: 42}) if __name__ __main__: q Queue p Process(targetworker, args(q,)) p.start result q.get # {result: 42} p.join对于大量数据的共享3.8 给出了更为高效的途径, 规避序列化所带来的开销。四、高并发 IO 的现代方案的核心思想为协作式多任务, 在单个线程内部, 借助await去实现显式地交出控制权, 从而能够使得事件循环于同一线程中对多个协程予以调度。import asyncio import aiohttp import time async def fetch(session, url): async with session.get(url) as resp: return await resp.text async def main: urls [https://httpbin.org/delay/1] * 50 async with aiohttp.ClientSession as session: tasks [fetch(session, url) for url in urls] results await asyncio.gather(*tasks) return results start time.perf_counter asyncio.run(main) print(f50个请求耗时: {time.perf_counter - start:.3f}s) # 约 1-2 秒50个请求, 每个请求延迟1秒, 按同步顺序执行的话, 需要50秒, 而并发执行的话, 大概只需1到2秒, 这取决于带宽以及连接池大小。这便是其核心优势: 以极低的线程开销处理海量的IO并发。4.1 async/await 的使用边界async def fetch_data: data await db.query(SELECT ...) return data asyncio.run(fetch_data) def main: data await fetch_data # SyntaxError4.2 同步代码与异步代码的桥接于实际项目里, 并非能够使所有代码皆为async。于异步的上下文环境之中, 去对同步函数展开调用import asyncio from concurrent.futures import ThreadPoolExecutor executor ThreadPoolExecutor(max_workers4) async def main: loop asyncio.get_running_loop result await loop.run_in_executor(executor, blocking_io_function)反转过来, 于同步代码里调用异步函数之时, 要使用 .run , 然而需留意, 不要进行嵌套, 也就是 .run 万万不可在那样一个业已有事件循环的线程当中再次去调用。五、并发模型选型决策场景 推荐方案 原因IO 密集网络请求、文件读写、数据库查询GIL 在 IO 等待时释放CPU 密集数值计算、图像处理、加密绕过 GIL利用多核高并发 IO万级连接如 、长轮询单线程事件循环无上下文切换开销中小规模 IO 并发百级已有同步代码库改造成本最低混合场景IO CPU 均有异步调度 CPU 密集型子任务需要共享大量内存状态进程间共享内存成本太高六、一个混合场景的实战示例设想存在这样一个任务, 从多个API那里拉取数据呢, 这属于IO密集型的, 之后针对每一条数据, 去做CPU密集型的解析。import asyncio from concurrent.futures import ProcessPoolExecutor def heavy_parse(raw_text: str) - dict: CPU 密集的解析逻辑。 return parsed async def fetch_and_parse(session, url, executor): async with session.get(url) as resp: raw await resp.text loop asyncio.get_running_loop return await loop.run_in_executor(executor, heavy_parse, raw) async def main(urls): executor ProcessPoolExecutor(max_workers4) async with aiohttp.ClientSession as session: tasks [fetch_and_parse(session, url, executor) for url in urls] results await asyncio.gather(*tasks) executor.shutdown return results存在一种模式, 它综合了具备高并发IO能力的某些特性, 以及拥有 CPU并行能力的特定方面, 这种模式适用于数据管道场景, 适用于ETL场景, 适用于批量API处理等场景。最后关键在于明确任务性质来进行并发选型, 任务的瓶颈究竟是在IO方面, 还是在CPU方面并发规模到底是百级别的, 还是万级别的? 现有代码到底是同步的, 还是异步的?几条原则

相关新闻

圈内流传 CTF 完整版入门教程,零基础直达竞赛,错过很难再找到

圈内流传 CTF 完整版入门教程,零基础直达竞赛,错过很难再找到

目录 一、CTF简介 二、CTF竞赛模式 三、CTF各大题型简介 四、CTF学习路线 4.1、初期 1、htmlcssjs(2-3天) 2、apachephp (4-5天) 3、mysql (2-3天) 4、python (2-3天) 5、burpsuite (…

2026/10/12 17:15:05 阅读更多 →
万字完整版网络安全服务手册,自学网安省下上千培训费,收藏备用

万字完整版网络安全服务手册,自学网安省下上千培训费,收藏备用

信息系统上线检测服务 信息系统上线检测服务主要由四部分组成:代码安全审计、安全漏洞扫描、安全配置核查和渗透性测试服务。通过全面、客观、深入的开展上线检测服务可对信息系统进行全方位安全评估分析,提供安全检测报告。 ▶检测流程 网络安全攻防演…

2026/10/10 18:00:44 阅读更多 →
开机三次卡“EB“之后,我用 OpCore-Simplify 跑通了 OpenCore EFI 自动生成

开机三次卡“EB“之后,我用 OpCore-Simplify 跑通了 OpenCore EFI 自动生成

开机三次卡"EB"之后,我用 OpCore-Simplify 跑通了 OpenCore EFI 自动生成 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 凌晨两…

2026/10/10 18:01:06 阅读更多 →

最新新闻

SpringBoot+Vue打造装饰工程管理系统:完整实战解析

SpringBoot+Vue打造装饰工程管理系统:完整实战解析

做装饰工程项目管理的系统,很多老牌软件公司都停留在单体架构时代,界面老旧、流程僵硬,项目经理宁可自己拉Excel表格也不愿意用。而新团队上来就搞微服务分布式,对一家中小型装饰公司来说,根本就是杀鸡用牛刀&#xff…

2026/10/13 7:36:08 阅读更多 →
Dalsa Nano相机C++采集例程:从接口配置到稳定抓帧

Dalsa Nano相机C++采集例程:从接口配置到稳定抓帧

简介:面向机器视觉、自动化检测与工业现场应用的 Dalsa Nano 相机 C 使用例程,以 GigE Vision 千兆网通信协议为基础,完整演示了相机从连接初始化、参数配置到实时图像采集、显示与保存的整个流程。压缩包内含 77 个文件,主体为 C…

2026/10/13 7:36:08 阅读更多 →
Hive多DISTINCT性能优化:用COLLECT_SET替代COUNT(DISTINCT)

Hive多DISTINCT性能优化:用COLLECT_SET替代COUNT(DISTINCT)

1. 项目概述:为什么“多个DISTINCT”会成为Hive SQL里的隐形性能杀手你有没有写过这样的Hive SQL?SELECT dt,COUNT(DISTINCT user_id) AS dau,COUNT(DISTINCT CASE WHEN is_paying 1 THEN user_id END) AS paying_users,COUNT(DISTINCT CASE WHEN chann…

2026/10/13 7:36:08 阅读更多 →
Flutter鸿蒙化适配:floor_generator桥接SQLite的完整实践与踩坑记录

Flutter鸿蒙化适配:floor_generator桥接SQLite的完整实践与踩坑记录

上个月在适配一个跨平台进销存项目到鸿蒙平台时,数据库层成了第一个硬骨头。这个项目的十几个业务模块全部建立在 floor 这个 ORM 框架之上,实体类、DAO 接口、查询条件都是现成的,本来以为 Flutter 能在鸿蒙环境跑起来,floor 也能…

2026/10/13 7:36:08 阅读更多 →
虚拟机 ens33 没有 IP?ifconfig 不显示网卡地址的排查与修复

虚拟机 ens33 没有 IP?ifconfig 不显示网卡地址的排查与修复

刚装好的虚拟机,开机之后敲下 ifconfig ,屏幕上来回翻的输出里只有 lo 和那个让人无语的 ens33 ,连个 IPv4 地址都不给。这个画面我见过太多次,第一次踩坑时还以为是虚拟网卡坏了,把虚拟机删了重建两回&#xff…

2026/10/13 7:36:08 阅读更多 →
E2B沙箱实战:AI智能体代码执行失控防护与排障指南

E2B沙箱实战:AI智能体代码执行失控防护与排障指南

1. 先搞清楚:AI 智能体为什么总能把沙箱跑崩上周有个朋友找我,说他的 AI 智能体项目出事了:Agent 在沙箱里跑了一段“看似无害”的 Python 脚本,结果沙箱彻底失联,连宿主机都差点被拖挂,最后只能重启整台机…

2026/10/13 7:35:07 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →