当前位置: 首页 > article >正文

深入理解 Python 的多进程编程 (Multiprocessing)

在 Python 中,multiprocessing 模块提供了多进程支持,是处理并发任务的一个核心工具。与多线程不同,多进程可以突破 GIL(Global Interpreter Lock,全局解释器锁)的限制,充分利用多核 CPU 进行并行计算。本文将详细介绍 Python 中的多进程编程,包括其基础用法、进程间通信、同步机制,以及与线程和协程的对比。


一、为什么选择多进程?

1. GIL 的限制

Python 的 GIL 限制了多线程的并行能力,同一时间只能有一个线程执行 Python 字节码。对于 CPU 密集型任务,多线程不能充分利用多核 CPU。

多进程通过创建独立的进程,每个进程拥有独立的 GIL,可以并行执行任务,适合需要大量计算的场景。

2. 适用场景

  • CPU 密集型任务:如科学计算、视频处理、大量数据的复杂运算。
  • 任务隔离需求:每个进程有独立的内存空间,减少了竞争资源的风险。

二、multiprocessing 的基础用法

1. 创建子进程

使用 multiprocessing.Process 类可以轻松创建子进程。

from multiprocessing import Process
import os

def worker(task_name):
    print(f"Task {task_name} is running in process {os.getpid()}")

if __name__ == "__main__":
    process1 = Process(target=worker, args=("A",))
    process2 = Process(target=worker, args=("B",))

    process1.start()
    process2.start()

    process1.join()
    process2.join()

    print("All processes completed")

输出

Task A is running in process 12345
Task B is running in process 12346
All processes completed

2. 进程池(multiprocessing.Pool

当需要管理大量进程时,使用进程池(Pool)可以更方便地分配和调度任务。

from multiprocessing import Pool

def worker(x):
    return x * x

if __name__ == "__main__":
    with Pool(4) as pool:  # 创建包含 4 个进程的进程池
        results = pool.map(worker, range(10))
    print(results)

输出

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

三、进程间通信

Python 提供了多种方式实现进程间通信,包括队列(Queue)、管道(Pipe)和事件(Event)。

1. 使用 Queue

Queue 提供了线程安全的队列,可以实现进程间的数据共享。

from multiprocessing import Process, Queue

def producer(queue):
    for i in range(5):
        queue.put(i)
        print(f"Produced: {i}")

def consumer(queue):
    while not queue.empty():
        item = queue.get()
        print(f"Consumed: {item}")

if __name__ == "__main__":
    q = Queue()

    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))

    p1.start()
    p1.join()

    p2.start()
    p2.join()

输出

Produced: 0
Produced: 1
Produced: 2
Produced: 3
Produced: 4
Consumed: 0
Consumed: 1
Consumed: 2
Consumed: 3
Consumed: 4

2. 使用 Pipe

Pipe 提供了双向通信的能力。

from multiprocessing import Process, Pipe

def sender(pipe):
    for i in range(5):
        pipe.send(i)
        print(f"Sent: {i}")
    pipe.close()

def receiver(pipe):
    while True:
        try:
            item = pipe.recv()
            print(f"Received: {item}")
        except EOFError:
            break

if __name__ == "__main__":
    parent_conn, child_conn = Pipe()

    p1 = Process(target=sender, args=(parent_conn,))
    p2 = Process(target=receiver, args=(child_conn,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

输出

Sent: 0
Sent: 1
Sent: 2
Sent: 3
Sent: 4
Received: 0
Received: 1
Received: 2
Received: 3
Received: 4

四、进程同步机制

多进程共享资源时需要同步工具,multiprocessing 提供了 LockEvent 等工具。

1. 使用 Lock

Lock 用于防止多个进程同时访问共享资源。

from multiprocessing import Process, Lock

counter = 0

def worker(lock):
    global counter
    for _ in range(100000):
        with lock:
            counter += 1

if __name__ == "__main__":
    lock = Lock()

    p1 = Process(target=worker, args=(lock,))
    p2 = Process(target=worker, args=(lock,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

    print(f"Final counter value: {counter}")

2. 使用 Event

Event 是一种简单的线程同步原语,用于让一个进程等待另一个进程发出信号。

from multiprocessing import Process, Event

def worker(event):
    print("Worker waiting for event to be set...")
    event.wait()  # 等待事件被设置
    print("Worker received event signal, starting work!")

if __name__ == "__main__":
    event = Event()

    p = Process(target=worker, args=(event,))
    p.start()

    print("Main process performing some setup...")
    import time
    time.sleep(2)

    print("Main process setting event.")
    event.set()  # 触发事件

    p.join()

输出

Worker waiting for event to be set...
Main process performing some setup...
Main process setting event.
Worker received event signal, starting work!

五、多进程、线程与协程的对比

特性多进程(multiprocessing多线程(threading协程(asyncio
适用场景CPU 密集型任务I/O 密集型任务,简单并发I/O 密集型任务,高性能并发
多核支持可充分利用多核受限于 GIL,无多核支持单线程实现并发,无多核支持
资源隔离每个进程独立,资源隔离性高线程共享内存,隔离性较低协程运行在同一线程,隔离性低
资源开销进程上下文切换开销高线程上下文切换开销较低协程更轻量,占用资源更少
通信方式队列(Queue)、管道(Pipe共享内存或队列事件循环或 asyncio.Queue
编程难度相对复杂较简单语法直观,使用方便

六、总结与推荐

  1. 选择多进程

    • 当任务是 CPU 密集型,需要并行处理时,优先考虑 multiprocessing
    • 适合需要进程隔离的场景,避免共享资源引发的数据竞争。
  2. 选择多线程

    • 适用于 I/O 密集型任务,例如文件操作、网络请求。
    • 如果任务需要共享内存并发处理,多线程更方便。
  3. 选择协程

    • 在高并发的 I/O 密集型任务 中(如异步网络请求),协程是最优选择。
    • 轻量、性能高,适合现代异步编程。

通过合理选择工具,可以在 Python 中充分利用多进程、多线程和协程的优势,打造高性能的并发程序。


http://www.kler.cn/a/501623.html

相关文章:

  • 【Rust】结构体定义域实例化
  • 计算机视觉算法实战——打电话行为检测
  • 21、Transformer Masked loss原理精讲及其PyTorch逐行实现
  • HTML5实现好看的中秋节网页源码
  • 如何稳定使用 O1 / O1 Pro,让“降智”现象不再困扰?
  • 相加交互效应函数发布—适用于逻辑回归、cox回归、glmm模型、gee模型
  • uniapp(接入智谱Ai完整示例)
  • 【git】-3 github创建远程仓库,上传自己的项目,下载别人的项目
  • ubuntu官方软件包网站 字体设置
  • client-go 的 QPS 和 Burst 限速
  • [Flutter] 使用ScrollController配合EasyRefresh实现列表预加载:在还未滑动到底部时加载下一页数据
  • 35_Lua基本语法
  • 学技术学英语:ELK是什么
  • 谷歌浏览器的音视频播放设置与优化
  • Mysql--运维篇--日志管理(连接层,SQL层,存储引擎层,文件存储层)
  • 如何使用商品详情API接口数据解析示例API接口URL
  • Kubernetes 基本概念
  • springboot图片上传存储至本地,返回前端地址回显
  • CSS语言的多线程编程
  • 递归构建树菜单节点
  • 键盘过滤驱动
  • 【WRF运行报错】总结WRF运行时报错及解决方案(持续更新)
  • MATLAB语言的面向对象编程
  • Spring Boot中的Profile是如何工作
  • CMA软件测试技术体系建设思路与框架设计分享
  • 3D 建模在游戏开发领域的应用现状和未来发展趋势如下