0

0

python如何实现多进程编程_python multiprocessing模块多进程编程实践

裘德小鎮的故事

裘德小鎮的故事

发布时间:2025-09-12 19:41:01

|

1030人浏览过

|

来源于php中文网

原创

Python多进程编程依赖multiprocessing模块,通过Process类或Pool进程池实现并行计算,有效规避GIL限制,适用于CPU密集型任务。

python如何实现多进程编程_python multiprocessing模块多进程编程实践

Python实现多进程编程主要依赖其内置的

multiprocessing
模块。这个模块提供了一套API,允许我们创建并管理多个独立的进程,每个进程都有自己独立的内存空间,互不干扰。这对于充分利用多核CPU资源,尤其是在处理CPU密集型任务时,能够有效规避Python全局解释器锁(GIL)带来的性能瓶颈,从而实现真正的并行计算。

解决方案

要实现Python多进程编程,核心就是使用

multiprocessing
模块。最基础的方式是利用
Process
类来创建并启动新进程,就像我们在操作系统层面启动一个独立程序一样。

举个例子,假设我们有一个耗时的计算任务:

import os
import time
from multiprocessing import Process, Queue

def compute_heavy_task(name, duration, output_queue=None):
    """一个模拟耗时计算的函数"""
    pid = os.getpid()
    print(f"进程 {name} (PID: {pid}) 启动,将运行 {duration} 秒。")
    start_time = time.time()
    result = 0
    for _ in range(int(duration * 1000000)): # 模拟CPU密集型计算
        result += 1
    end_time = time.time()
    print(f"进程 {name} (PID: {pid}) 完成,耗时 {end_time - start_time:.2f} 秒。")
    if output_queue:
        output_queue.put(f"结果来自 {name}: {result}")

if __name__ == '__main__':
    print(f"主进程 (PID: {os.getpid()}) 启动。")

    # 创建一个队列用于进程间通信
    results_queue = Queue()

    # 创建并启动多个进程
    process1 = Process(target=compute_heavy_task, args=('Worker-1', 2, results_queue))
    process2 = Process(target=compute_heavy_task, args=('Worker-2', 3, results_queue))
    process3 = Process(target=compute_heavy_task, args=('Worker-3', 1, results_queue))

    process1.start() # 启动进程1
    process2.start() # 启动进程2
    process3.start() # 启动进程3

    # 等待所有子进程完成
    process1.join()
    process2.join()
    process3.join()

    print("所有子进程已完成。")

    # 从队列中获取结果
    while not results_queue.empty():
        print(results_queue.get())

    print("主进程结束。")

在这个例子里,我们定义了一个

compute_heavy_task
函数,它模拟了一个CPU密集型操作。然后,在
if __name__ == '__main__':
块中(这在Windows上是必须的,避免子进程无限递归创建),我们创建了三个
Process
实例,分别指向这个函数,并通过
args
传递参数。
start()
方法启动进程,而
join()
方法则让主进程等待子进程执行完毕。这里还引入了
Queue
来演示进程间的简单通信,子进程可以将结果放入队列,主进程再从中取出。

立即学习Python免费学习笔记(深入)”;

除了直接使用

Process
类,
multiprocessing
模块还提供了一个更高级、更方便的抽象——
Pool
Pool
可以创建一个工作进程池,自动管理这些进程的生命周期,并提供了
map
apply
starmap
等方法,非常适合并行处理一系列独立任务。

import os
import time
from multiprocessing import Pool

def square(x):
    """一个简单的计算函数"""
    pid = os.getpid()
    print(f"进程 {pid} 正在计算 {x} 的平方...")
    time.sleep(0.5) # 模拟一些工作
    return x * x

if __name__ == '__main__':
    print(f"主进程 (PID: {os.getpid()}) 启动。")
    data = [1, 2, 3, 4, 5, 6, 7, 8]

    # 创建一个包含4个工作进程的进程池
    # 默认情况下,Pool会使用os.cpu_count()个进程
    with Pool(processes=4) as pool:
        # 使用map方法将data中的每个元素并行地传递给square函数
        results = pool.map(square, data)

    print("所有计算已完成。")
    print("结果:", results)
    print("主进程结束。")

Pool
map
方法与内置的
map
函数类似,但它会将可迭代对象中的元素分发给进程池中的各个进程并行处理,然后收集所有结果。这大大简化了并行任务的管理。

为什么在Python中多进程比多线程更适合CPU密集型任务?

这大概是很多初学者都会困惑的问题,甚至连我刚开始接触Python并发编程时也一度搞不清楚。说白了,核心原因在于Python的全局解释器锁(Global Interpreter Lock,简称GIL)。

GIL是CPython解释器(也就是我们最常用的Python实现)的一个特性,它在任何时刻都只允许一个线程执行Python字节码。这意味着,即使你的机器有多个CPU核心,一个Python进程内的多个线程也无法真正并行地执行Python代码。它们会轮流获取GIL,交替执行,这对于I/O密集型任务(比如网络请求、文件读写,因为等待I/O时线程会释放GIL)来说影响不大,甚至能提高效率。

然而,对于CPU密集型任务(比如复杂的数学计算、数据处理),线程之间会频繁地争抢GIL。一个线程好不容易拿到GIL开始计算,没多久可能就被迫释放GIL让给其他线程,然后又得重新竞争。这种频繁的上下文切换和锁的竞争,反而会引入额外的开销,导致多线程版本的程序可能比单线程版本还要慢。这听起来有点反直觉,但这就是GIL的现实。

Magic AI Avatars
Magic AI Avatars

神奇的AI头像,获得200多个由AI制作的自定义头像。

下载

多进程则不同。每个进程都有自己独立的Python解释器实例和内存空间。这意味着每个进程都有自己独立的GIL,它们之间互不影响。当一个进程执行CPU密集型任务时,它拥有自己的GIL,可以完全占用一个CPU核心进行计算,而其他进程也可以同时在其他CPU核心上独立运行。因此,多进程能够真正地利用多核CPU的并行计算能力,是解决Python中CPU密集型任务性能瓶颈的有效手段。我个人觉得,理解GIL是深入Python并发编程的第一步,它直接决定了你选择多进程还是多线程。

使用multiprocessing模块时,有哪些常见的陷阱和最佳实践?

multiprocessing
模块虽然强大,但在实际使用中也有些“坑”和需要注意的地方。

常见的陷阱:

  1. 进程间数据共享的误解: 这是最常见的错误。进程拥有独立的内存空间,所以父进程的变量在子进程中是独立的副本,直接修改子进程中的变量不会影响父进程或其他子进程。如果需要共享数据,必须使用
    multiprocessing
    模块提供的特定机制,如
    Queue
    (队列)、
    Pipe
    (管道)、
    Value
    (共享值)、
    Array
    (共享数组)或者
    Manager
    (管理器)。忘记这一点会导致数据不一致或逻辑错误。
  2. Pickling问题: 进程间传递对象(通过
    Queue
    Pipe
    Pool
    的参数/返回值)时,这些对象必须是可序列化的(pickleable)。如果尝试传递一个不可序列化的对象(比如lambda函数、嵌套函数、某些自定义的复杂对象实例),程序会抛出
    TypeError
  3. Windows系统下的
    if __name__ == '__main__':
    在Windows上,当你启动一个新进程时,Python会导入你的主模块。如果没有
    if __name__ == '__main__':
    这个判断,子进程在导入模块时会再次执行所有顶层代码,包括创建新进程的代码,导致无限递归创建进程,直到系统资源耗尽。在Linux/macOS上,通常使用
    fork
    方式创建进程,子进程会直接复制父进程的内存空间,所以不强制要求,但为了跨平台兼容性,始终使用这个判断是最佳实践。
  4. 死锁和竞态条件: 即使进程间内存隔离,但当多个进程尝试访问或修改共享资源(如
    Queue
    Lock
    )时,仍然可能发生死锁(进程相互等待对方释放资源)或竞态条件(操作顺序不确定导致结果错误)。这需要仔细设计进程间通信和同步机制。
  5. 进程创建开销: 创建一个新进程比创建线程的开销要大得多,因为它需要复制父进程的内存空间(或在
    fork
    模式下建立映射),并启动一个新的解释器。因此,不适合频繁地创建和销毁大量进程。

最佳实践:

  1. 优先使用
    Pool
    对于任务分发和结果收集的场景,
    Pool
    比手动管理
    Process
    实例要简洁高效得多。它会自动管理进程的生命周期,提供
    map
    apply
    等方便的接口。
  2. 明确IPC策略: 如果需要进程间通信,提前规划好使用哪种IPC机制。
    Queue
    适合生产者-消费者模式,
    Pipe
    适合双向通信,
    Lock
    用于同步,
    Manager
    则能创建可在多个进程间共享的Python对象。
  3. 最小化进程间通信: 进程间通信是有开销的。尽量设计任务,使得每个子进程能够独立完成大部分工作,只在必要时进行通信或共享少量数据,避免过度同步。
  4. 合理设置进程数量: 对于CPU密集型任务,通常将进程数量设置为CPU的核心数或略多一点(例如
    os.cpu_count() + 1
    ),以充分利用硬件资源。过多的进程反而会因上下文切换开销而降低性能。
  5. 错误处理和超时机制: 在实际应用中,子进程可能会出错或长时间无响应。考虑使用
    try...except
    块捕获子进程中的异常,并为
    join()
    Pool
    方法设置超时参数,防止程序无限等待。
  6. 考虑
    concurrent.futures.ProcessPoolExecutor
    这是Python标准库
    concurrent.futures
    模块提供的一个更高级的抽象,它提供了与
    threading.ThreadPoolExecutor
    相似的接口,可以更方便地在进程池中提交任务并获取结果,代码通常更简洁易读。

除了multiprocessing,Python还有哪些处理并发任务的工具或模式?

Python处理并发任务的工具和模式远不止

multiprocessing
一个,它们各有侧重,适用于不同的场景。

  1. threading
    模块(多线程): 这是Python处理并发最直接的方式之一。它允许在一个进程内创建多个执行线程。如前所述,由于GIL的存在,
    threading
    在CPU密集型任务上无法实现真正的并行,但它在I/O密集型任务(如网络请求、文件读写、等待数据库响应等)中表现出色。当一个线程等待I/O时,它会释放GIL,允许其他线程运行,从而提高整体吞吐量。

  2. asyncio
    模块(异步IO/协程):
    asyncio
    是Python处理单线程并发的强大工具,它基于协程(coroutine)和事件循环(event loop)。它不创建新的线程或进程,而是在单个线程中通过协作式多任务(cooperative multitasking)来实现并发。当一个协程遇到I/O等待时,它会“暂停”执行并将控制权交还给事件循环,事件循环会去执行其他就绪的协程。这种方式非常适合高并发的I/O密集型应用,比如网络服务器、爬虫等,因为它避免了线程/进程切换的开销,效率极高。

  3. concurrent.futures
    模块: 这个模块提供了一个高层次的接口来异步执行可调用对象。它包含两个主要的执行器:

    • ThreadPoolExecutor
      :基于
      threading
      模块,用于线程池。
    • ProcessPoolExecutor
      :基于
      multiprocessing
      模块,用于进程池。 它提供
      submit()
      方法提交任务,并返回
      Future
      对象,通过
      Future
      对象可以查询任务状态、获取结果或捕获异常。
      concurrent.futures
      极大地简化了多线程和多进程编程的复杂性,提供了一致的API。
  4. 第三方库,例如

    joblib
    在科学计算和数据分析领域,
    joblib
    库是一个非常实用的工具。它提供了一个简单的
    Parallel
    类,可以方便地将
    for
    循环并行化,底层可以选择使用多线程或多进程。对于那些需要对大量数据进行独立处理的场景,
    joblib
    能显著提升效率。

对我来说,选择哪种工具,很大程度上取决于任务的性质。CPU密集型任务,我果断会考虑

multiprocessing
concurrent.futures.ProcessPoolExecutor
。I/O密集型任务则在
threading
asyncio
之间权衡,后者尤其适合需要处理大量并发连接的高性能网络服务。如果只是简单的并行化循环,
joblib
Parallel
用起来也相当顺手。理解这些工具的优缺点,才能在实际项目中做出最合适的选择。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

847

2023.08.22

lambda表达式
lambda表达式

Lambda表达式是一种匿名函数的简洁表示方式,它可以在需要函数作为参数的地方使用,并提供了一种更简洁、更灵活的编码方式,其语法为“lambda 参数列表: 表达式”,参数列表是函数的参数,可以包含一个或多个参数,用逗号分隔,表达式是函数的执行体,用于定义函数的具体操作。本专题为大家提供lambda表达式相关的文章、下载、课程内容,供大家免费下载体验。

215

2023.09.15

python lambda函数
python lambda函数

本专题整合了python lambda函数用法详解,阅读专题下面的文章了解更多详细内容。

192

2025.11.08

Python lambda详解
Python lambda详解

本专题整合了Python lambda函数相关教程,阅读下面的文章了解更多详细内容。

61

2026.01.05

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1926

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

656

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2395

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

47

2026.01.19

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 10.6万人学习

Git 教程
Git 教程

共21课时 | 4.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号