Python进程详解

news/2025/6/19 18:16:31

进程是指一个程序的运行实例,它是一个独立的执行环境,拥有自己的内存空间、系统资源和执行状态。Python 中的进程可以通过标准库中的 multiprocessing 模块来创建和管理。

  • 独立性:每个进程都有自己的独立内存空间,一个进程的变量和数据不会直接被其他进程访问。
  • 并发性:多个进程可以同时运行,操作系统会分配CPU时间片来执行它们,从而实现并发运行的效果。
  • 资源占用:进程是系统资源分配的基本单位,它会占用一定的 CPU 时间、内存空间、文件句柄等资源。

进程和线程的对比:

维度进程线程
资源分配独立内存空间,资源占用大共享进程内存,资源占用小
并发性真正的并行(多核 CPU)受 GIL 限制,多线程为并发而非并行
通信成本需通过 IPC,复杂且效率低可直接共享内存,通信简单高效
适用场景CPU 密集型、需要隔离的任务I/O 密集型、轻量级任务

基本创建方法

from multiprocessing import Process
import osdef worker():print(f"子进程ID:{os.getpid()},父进程ID:{os.getppid()}")if __name__ == "__main__":  # Windows系统必须添加此判断p = Process(target=worker)p.start()  # 启动子进程p.join()   # 等待子进程结束

可以看出和Python线程的使用方式上十分一致。

📢:在 Windows 系统中,Python 借助 spawn 方式来启动新进程。这一过程意味着新进程会重新执行整个 Python 脚本。要是主逻辑没有被 if __name__ == "__main__" 包裹起来,新进程在启动时就会再次运行创建进程的代码,进而造成递归循环,最终引发错误或者死锁。Linux 和 macOS 系统采用的是 fork 方式来启动新进程。在这种方式下,子进程会复制父进程的内存空间,因此无需 if __name__ == "__main__" 也能正常运行。不过,为了保证代码在不同平台上都具有可移植性,最好还是统一添加这一保护机制。

spawn和fork两种启动新进程的方式有兴趣的读者可以进一步探究,这里不作讨论。

进程锁

在多进程环境中,多个进程可能会同时访问或修改共享资源(如文件、队列、数据库等)。如果没有适当的同步机制,可能会出现以下问题:

  • 数据竞争(Race Condition):多个进程同时修改共享资源,导致最终结果不确定。
  • 状态不一致:进程之间的操作顺序无法保证,可能导致程序状态混乱。
  • 资源损坏:多个进程同时写入同一个文件或数据库,可能导致数据损坏。

进程锁的使用方式与线程锁类似:

from multiprocessing import Process, Lockdef worker(lock, name):with lock:  # 自动获取和释放锁print(f"{name} is working")"""手动管理就是:lock.acquire()  # 获取锁try:print(f"{name} is working")finally:lock.release()  # 释放锁"""if __name__ == "__main__":lock = Lock()processes = []for i in range(5):p = Process(target=worker, args=(lock, f"Process-{i}"))processes.append(p)p.start()for p in processes:p.join()

进程锁的实现依赖于操作系统的同步机制。在底层,锁通常是一个互斥量(Mutex),它是一个二进制信号量,用于控制对共享资源的访问。互斥量的状态可以是“锁定”或“解锁”:

  • 当一个进程请求锁时,如果锁处于“解锁”状态,它会被设置为“锁定”,进程可以继续执行。
  • 如果锁已经被其他进程占用(处于“锁定”状态),请求锁的进程会被阻塞,直到锁被释放。

操作系统会负责管理这些锁的状态,并在锁被释放时唤醒等待的进程。

同线程一样,进程还有信号量、事件、条件等同步机制,这里不作讨论。

进程间通信(IPC)

在线程通信那节(8.4)我们了解过queue.Queue 是 Python 标准库中用于线程间通信的线程安全队列,但它不能用于进程。

由于进程是独立的执行单元,它们拥有各自的内存空间,因此进程间通信需要通过特定的机制来实现。Python 提供了多种进程间通信的方式,包括管道(Pipes)、队列(Queues,底层也用到了管道)、共享内存(Shared Memory)、消息队列(Message Queues)、信号(Signals)等。

这里我们只讨论进程间队列。

multiprocessing.Queue 是 Python 的 multiprocessing 模块中用于实现进程间通信的一个队列类。它允许在不同的进程之间安全地传递数据,即使这些进程运行在不同的内存空间中。

multiprocessing.Queue 的实现基于操作系统的管道(Pipe)和锁机制:

  • 管道(Pipe):管道是一种半双工的通信机制,数据只能沿着一个固定的方向流动,即一端用于写入数据,另一端用于读取数据。multiprocessing.Queue 内部使用了匿名管道,用于在进程之间传输数据。数据被序列化后通过管道发送到另一个进程。
  • 锁(Lock):用于确保队列操作的原子性,防止多个进程同时对队列进行操作。

具体来说:

  • 当一个进程调用 put 方法时(此时会上锁),数据会被序列化并发送到管道中。
  • 当另一个进程调用 get 方法时(此时会上锁),它会从管道中读取数据并反序列化。
from multiprocessing import Process, Queue
import timedef producer(queue):for i in range(5):print(f"Producer putting {i} into the queue")queue.put(f"Message {i}")time.sleep(0.1)  # 模拟生产者的工作时间def consumer(queue):while True:item = queue.get()if item is None:  # 使用 None 作为结束信号breakprint(f"Consumer got {item} from the queue")time.sleep(0.2)  # 模拟消费者的工作时间if __name__ == "__main__":queue = Queue()# 创建生产者和消费者进程producer_process = Process(target=producer, args=(queue,))consumer_process = Process(target=consumer, args=(queue,))# 启动进程producer_process.start()consumer_process.start()# 等待生产者完成producer_process.join()# 向队列中放入结束信号queue.put(None)# 等待消费者完成consumer_process.join()

https://dhexx.cn/news/show-5537657.html

相关文章

【大厂机试题解法笔记】报文响应时间

题目 IGMP 协议中,有一个字段称作最大响应时间 (Max Response Time) ,HOST收到查询报文,解折出 MaxResponseTime 字段后,需要在 (0,MaxResponseTime] 时间 (s) 内选取随机时间回应一个响应报文,如果在随机…

PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

在构建任何动态、数据驱动的Web API时,一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说,深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言,以及学会如何在Python中操作数据库,是…

删除远程已经不存在但本地仍然存在的Git分支

1. 获取远程分支列表 首先,确保你获取了远程仓库的最新分支信息: git fetch -p -p 参数会自动清理本地仓库中那些在远程已经被删除的分支的引用。 2. 查看本地分支与远程分支的对比 运行以下命令来查看哪些本地分支没有对应的远程分支: …

【使用LLM搭建系统】5 处理输入: 链式 Prompt Chaining Prompts

本章内容主要介绍了将复杂任务拆分为多个子任务(链式Prompt)的方法及其优势。尽管高级语言模型像GPT - 4擅长一次性遵循复杂指令,但有时拆分任务更可取。通过两个比喻来阐述原因: 一次烹饪复杂菜肴与分阶段烹饪:一次性…

【CANN全新升级】CANN创新MLAPO算子,DeepSeek模型推理效率倍增

MoE模型中的MLA架构 DeepSeek系列模型凭借其创新性的MLA(Multi-Head Latent Attention)架构,替代了传统的MHA(Multi Head Attention),显著降低了推理时的KV Cache开销,大幅提升了推理效率&…

SDC命令详解:使用set_wire_load_model命令进行约束

相关阅读 SDC命令详解https://blog.csdn.net/weixin_45791458/category_12931432.html?spm1001.2014.3001.5482 目录 指定线负载模型名 指定搜索库 指定最大、最小条件 指定对象列表 set_wire_load_model命令用于显式指定一个线负载模型(设置了对象的wire_loa…

SQL进阶之旅 Day 22:批处理与游标优化

【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…

pymilvus

一.pymilvus介绍 🚀 pymilvus 是什么? pymilvus 是连接和操作 Milvus 向量数据库的 Python SDK,用于处理大规模向量数据的存储、索引和搜索。 🏗️ Milvus 向量数据库 什么是 Milvus? 🔍 专业向量数据…

C/C++ 面试复习笔记(5)

1.用户态和内核态切换的开销来自哪里?如何减少这种开销? 主要开销: 上下文保存与恢复:需保存/恢复寄存器、堆栈等状态(约数百CPU周期)。 CPU 模式切换:从用户态到内核态的权限检查及模式切换…

CppCon 2015 学习:Time Programming Fundamentals

Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…