理解python进程
·
进程 - CPU密集型王者
概述
- 操作系统资源分配的基本单位(注意这里的 资源分配 只是分配资源)
- 硬件多核前提下,Python multiprocessing 多进程可以实现真正并行;单进程只能并发,无法并行
- python的多进程会 真正启动底层的多个系统进程,每个进程拥有独立的内存空间和完整的python解释器状态
- 这完美规避了全局解释器锁(GIL)的限制,能够真正利用多核CPU
理解
多进程中的每个进程都是单线程 / 多进程中的每个进程都是多线程 哪种能规避GIL限制?
- 结论:
只有多进程中的每个进程都是单线程能规避 - 解释:
multiprocessing 开辟多个操作系统进程
每一个进程内部,都拥有独立 Python 解释器、独立 GIL
GIL 的生效范围:仅局限当前进程内部,进程和进程互不影响 - 举例:
- 场景 A:每个子进程只开 1 条线程
- 进程内只有一个线程,不存在线程争抢 GIL。
- 操作系统可以把多个进程调度到多核,真正并行执行 CPU 任务,充分规避 GIL 瓶颈
- 场景 B:某一个子进程内部继续创建多条threading线程
- ⚠️ 这个进程内部依旧受本进程自带 GIL 约束!
- 在同一个进程里面,无论你开多少线程,同一时刻依旧只能一条线程执行 CPU 计算。
- 场景 A:每个子进程只开 1 条线程
- 认识GIL:
- GIL 属于单个 Python 解释器实例,一个进程对应一套解释器、一把 GIL;
多进程方案能利用多核,本质是多个独立进程可被调度到不同 CPU 核心;
如果某个进程内部使用多线程,该进程内部依旧无法并行计算,逃不开当前进程的 GIL;所以做 CPU 密集运算的最佳实践:每个工作进程保持单线程
- GIL 属于单个 Python 解释器实例,一个进程对应一套解释器、一把 GIL;
- 多进程适用场景:
- 适合干那些极耗费脑力/体力的活 例如图像处理、加密解密、大规模矩阵预算计算
- 缺点:
- 创建进程太重了,消耗内存大
多进程演示
import multiprocessing
import time
def calculate_heavy_task(name):
print(f"进程 {name} 开始疯狂计算...")
time.sleep(2) # 模拟耗时计算
print(f"进程 {name} 计算完成!")
if __name__ == '__main__':
start = time.time()
# 创建两个独立的进程
p1 = multiprocessing.Process(target=calculate_heavy_task, args=("A",))
p2 = multiprocessing.Process(target=calculate_heavy_task, args=("B",))
p1.start()
p2.start()
p1.join()
p2.join()
end = time.time()
print("所有计算任务结束!", end - start)
深度剖析 - 进程的作用
线程才是真正干活的,也能调用多核心,那把“进程”这个概念完全砍掉,让所有线程直接在操作系统里裸奔,不是更轻量、更直接吗?
结论:
如果只有线程,计算机一天得崩溃一百次。线程虽然会干活,但它是个“没家产、没记性、没边界”的打工人。进程的出现,本质上是为了给线程提供“生存空间”、“保护伞”和“规矩”。
资源分配的“大管家”(给线程分家产)
- 线程自己是几乎不拥有任何系统资源的。它没有独立的内存,不知道哪里可以存数据。
- 进程:当你启动一个程序时,进程负责向操作系统申请大笔的“家产”——包括独立的虚拟内存空间(堆、代码段、数据段)、打开的文件句柄、网络套接字、环境变量等。
- 线程怎么用:进程申请到这笔家产后,住在里面的所有线程共享这笔家产。如果没了进程这个大管家,线程一出生就两眼一抹黑,连把变量存在内存的哪个地方都不知道。
内存隔离的“防火墙”(防止线程互相投毒)
- 这是有进程最关键的物理原因:安全与稳定性。
- 如果只有线程:
- 所有软件的线程都混在同一个内存池里。某天你写了一个有 Bug 的 C++ 线程,不小心写错了一个指针(比如野指针),直接把隔壁微信线程或者银行支付线程的内存数据给覆盖抹掉了。这不仅会导致系统极度不稳定,还带来了致命的隐私和安全隐患。
- 有了进程之后:
操作系统给每个进程划分了绝对隔离的虚拟内存地址空间。
微信进程只能访问微信的内存。
浏览器进程只能访问浏览器的内存。
底层铁律:进程 A 的线程绝对不可能直接去读写进程 B 的内存。如果某个线程敢越界,操作系统的硬件保护机制(MMU)会当场触发“段错误(Segmentation Fault)”,直接把这个犯规的进程一枪干掉,而其他进程毫发无损。
- 代码演示
# 进程防火墙的例子 import threading import time # ========== 全部线程共享同一进程内的全局变量(共享内存区域) ========== account_balance = 1000 # 银行支付线程使用 wechat_msg_cache = [] # 微信线程使用 # 线程1:银行支付线程,持续扣款 def bank_pay_thread(): global account_balance for _ in range(10): if account_balance >= 100: account_balance -= 100 print(f"【支付线程】扣款成功,当前余额:{account_balance}") time.sleep(0.01) # 线程2:微信线程,持续接收消息存入缓存 def wechat_thread(): global wechat_msg_cache for i in range(10): wechat_msg_cache.append(f"消息{i}") print(f"【微信线程】存入消息,缓存长度:{len(wechat_msg_cache)}") time.sleep(0.01) # 线程3:有BUG的线程!无保护并发胡乱修改共享全局数据 # 类比C++野指针乱覆盖隔壁线程内存 def bug_thread(): global account_balance, wechat_msg_cache for _ in range(8): # 恶意/错误篡改支付线程的余额数据 account_balance = 0 # 清空微信线程的消息缓存 wechat_msg_cache.clear() print("【BUG线程】篡改了共享内存里的数据!") time.sleep(0.02) if __name__ == "__main__": t1 = threading.Thread(target=bank_pay_thread) t2 = threading.Thread(target=wechat_thread) t3 = threading.Thread(target=bug_thread) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print("\n最终结果:") print(f"账户余额 = {account_balance}") print(f"微信消息列表 = {wechat_msg_cache}")
权限与身份的“代言人”(防止流氓软件越权)
- 操作系统在管理安全策略时,只认进程,不认线程。
- 你在电脑上右键“以管理员身份运行”,或者在手机上弹窗提示“是否允许微信访问相册”,这个权限是赋予进程的。
- 进程就像是一张“通行证”。有了这张通行证,住在这个进程里的所有打工线程才能合法地去读写特定文件、访问网络。如果只有线程,操作系统根本没办法精细地管理哪个线程是安全的、哪个线程是木马病毒。
软件层面的“生命周期管理”
- 从开发和维护的角度来看,进程是软件存在的最小实体。
- 你在任务管理器里看到的、能手动“结束任务”的,都是进程。
- 如果一个复杂的软件(包含几百个线程)卡死了,你只需要在任务管理器里把这个进程杀掉,操作系统就会自动回收这个进程持有的所有内存、关闭它打开的所有文件,干净利落。
- 如果只有线程,你可能得在几千个乱七八糟的线程里,苦苦寻找哪几个是属于刚才那个卡死软件的,根本无法管理。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)