一文分享Python进程间通信的3种正确方法

 更新时间:2026年07月07日 08:58:24   作者:站大爷IP  
这篇文章主要介绍了Python中实现进程间通信的方法,3种正确姿势让子进程与主进程数据不迷路,Queue、Manager、SharedMemory三大方案实现详解,助你轻松实现高性能数据处理,告别变量为0的尴尬

一个让我抓狂的数据处理任务

上个月我接了个活,处理200万条用户行为日志。每条日志都要做格式清洗、字段提取、类型转换。单线程跑完要13秒,但服务器是8核的,我心想:用多进程总该起飞了吧?

代码很快写好了:

import multiprocessing as mp

CONFIG = {"date_format": "%Y-%m-%d", "max_len": 100}
results = []  # 用来收集所有子进程的结果

def process_line(line):
    # 用CONFIG里的配置处理数据
    cleaned = do_clean(line, CONFIG)
    results.append(cleaned)  # 试图把结果存到全局列表里
    return cleaned

if __name__ == "__main__":
    with mp.Pool(4) as pool:
        pool.map(process_line, data)
    print(len(results))  # 0!居然是0!

我盯着屏幕上的“0”,懵了整整五分钟。

主进程里的results明明是全局列表,子进程往里面加了数据,为什么主进程拿不到?

原因很简单:进程之间是隔阂的。每个子进程有自己独立的内存空间,它修改的是自己那一份results的拷贝,主进程的results从来没变过。

今天我就把Python多进程里“让变量不迷路”的3种正确姿势讲清楚。

为什么子进程总是“拿不到”数据?

在讲解决方案之前,先花一分钟搞清楚问题根源。

当你用multiprocessing.ProcessPool创建子进程时,Python会:

  1. 启动一个新的Python解释器(相当于重新打开了一个Python程序)
  2. 在新的解释器里重新执行你的代码(包括导入模块、定义函数、执行全局代码)
  3. 子进程拥有独立的内存空间,和主进程完全隔开

操作系统不允许一个进程随便读写另一个进程的内存。所以你在主进程里定义的CONFIGresultsglobal_counter,子进程都看不见。即使子进程“看起来”访问了同名变量,它操作的也是一份独立的拷贝。这就是“变量迷路”的根本原因。

搞清楚了“为什么”,接下来看“怎么办”。Python提供了3种主流方案,让数据在进程之间安全地流通。

姿势一:Queue——像传送带一样传递数据

multiprocessing.Queue是最常用的进程间通信方式。它像一个传送带,一个进程把数据放上去,另一个进程从另一端取下来。

适用场景:生产者-消费者模式、任务分发、结果收集。

为什么用它:Queue内部处理了所有同步问题,多个进程可以安全地往里面放数据和取数据。它是线程安全和进程安全的。

怎么写

from multiprocessing import Process, Queue

def worker(q):
    # 从队列取数据,直到收到结束信号
    while True:
        item = q.get()
        if item is None:  # None作为“没有更多任务”的信号
            break
        result = item * 2
        q.put(result)  # 把处理结果放回队列

if __name__ == "__main__":
    q = Queue()
    
    # 启动工作进程
    p = Process(target=worker, args=(q,))
    p.start()
    
    # 主进程往队列里放任务
    for i in range(10):
        q.put(i)
    
    # 发送结束信号
    q.put(None)
    p.join()
    
    # 从队列里拿结果
    while not q.empty():
        print(q.get())

关键点Queueput()get()都是阻塞操作,如果队列满了,put()会等待;如果队列空了,get()会等待。你可以用q.put(None)作为“任务结束”的信号,让子进程优雅退出。

什么时候用:任务数量不确定、需要动态分发工作、多个生产者和多个消费者。

姿势二:Manager——让多个进程共享同一个“黑板”

如果你需要多个进程共享同一个列表、字典或其他复杂数据结构,Manager是更直接的选择。

适用场景:多个进程需要读写同一个数据容器(比如共享任务列表、共享配置字典)。

为什么用它Manager会启动一个独立的“管理器进程”,所有对共享对象的操作都通过这个进程来协调。你操作共享列表就像操作普通列表一样,但背后会自动进行进程间通信和同步。

怎么写

from multiprocessing import Process, Manager

def worker(shared_list, shared_dict, value):
    shared_list.append(value)
    shared_dict[value] = value * 2

if __name__ == "__main__":
    with Manager() as manager:
        # 创建共享的列表和字典
        shared_list = manager.list(["初始数据"])
        shared_dict = manager.dict()
        
        processes = []
        for i in range(5):
            p = Process(target=worker, args=(shared_list, shared_dict, i))
            processes.append(p)
            p.start()
        
        for p in processes:
            p.join()
        
        print(list(shared_list))  # ['初始数据', 0, 1, 2, 3, 4]
        print(dict(shared_dict))   # {0: 0, 1: 2, 2: 4, 3: 6, 4: 8}

关键点Manager支持的类型很丰富——listdictNamespaceLockQueue等。但需要注意,Manager的每次读写操作都有进程间通信的开销,性能比直接访问内存慢。如果你只需要共享简单的数值,用ValueArray效率更高。

什么时候用:需要共享复杂数据结构、代码可读性比极致性能更重要、数据量不大。

姿势三:SharedMemory——直达内存的“高速公路”

如果你的数据量很大,或者对性能有要求,SharedMemory是最优解。它让多个进程直接读写同一块物理内存,不需要序列化和反序列化,速度最快。

适用场景:大量数据共享、高性能计算、NumPy数组跨进程共享。

为什么用它:相比Queue和Manager,SharedMemory避开了序列化/反序列化和复制数据的开销,直接在内存层面共享数据,性能优势极其明显。

怎么写

from multiprocessing import shared_memory
import numpy as np

# 主进程:创建共享内存
shm = shared_memory.SharedMemory(create=True, size=1024)
# 用numpy访问这块内存
arr = np.ndarray((256,), dtype=np.int32, buffer=shm.buf)
arr[:] = np.arange(256)  # 存入数据

# 子进程可以拿到shm.name后连接到同一块内存
# shm_b = shared_memory.SharedMemory(name=shm.name)
# arr_b = np.ndarray((256,), dtype=np.int32, buffer=shm_b.buf)
# 读写arr_b就能直接修改arr的数据

# 清理
shm.close()
shm.unlink()  # 释放共享内存块

进阶用法:用SharedMemoryManager配合with语句,自动管理共享内存的生命周期,避免忘记unlink导致内存泄漏:

from multiprocessing.managers import SharedMemoryManager

with SharedMemoryManager() as smm:
    sl = smm.ShareableList(range(2000))
    # 多个进程可以共享sl
    # 退出with块时,所有共享内存自动释放

如果你用NumPy做科学计算,还可以用第三方库shared-ndarray2进一步简化操作。

什么时候用:数据量大(MB级以上)、对性能敏感、需要频繁读写共享数据。

一张图帮你选

通信方式适用场景性能易用性
Queue任务分发、结果收集中等(需序列化)⭐⭐⭐⭐⭐
Manager共享复杂数据结构较慢(进程间协调)⭐⭐⭐⭐
SharedMemory大量数据、高性能极快(零拷贝)⭐⭐⭐(需管理内存)

一句话决策

  • 只是传递任务和结果 → 用Queue
  • 需要共享列表/字典,数据量不大 → 用Manager
  • 共享大数组、追求性能 → 用SharedMemory

回到开头的那个Bug

我那个数据处理任务最后用了Queue。主进程把200万行数据分批放进Queue,4个子进程轮流取数据、处理、把结果放回另一个Queue。主进程再从结果Queue里收集所有清洗后的数据。

改了之后,耗时从13秒降到了3.6秒,而且再也不会出现“results是0”的尴尬了。

记住:多进程之间是隔阂的,变量不会自动共享。想让数据不迷路,就得用对姿势——Queue传递消息,Manager共享容器,SharedMemory直达内存。选对方案,你的多进程程序才能真正跑起来。

到此这篇关于一文分享Python进程间通信的3种正确方法的文章就介绍到这了,更多相关Python进程通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python用reduce和map把字符串转为数字的方法

    python用reduce和map把字符串转为数字的方法

    最近在复习高阶函数的时候,有一道题想了半天解不出来。于是上午搜索资料,看了下别人的解法,发现学习编程,思维真的很重要。下面这篇文章就来给大家介绍了python利用reduce和map把字符串转为数字的思路及方法,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-12-12
  • 利用Python批量生成任意尺寸的图片

    利用Python批量生成任意尺寸的图片

    不知道大家有没有遇到过,因检验需要1000张,分别从1*1到1000*1000像素的图片。搜索一番过后发现还是Python实现比较方便,遂决定用Python实现这一功能,下面分享给大家,有需要的可以参考。
    2016-08-08
  • Python爬虫基础之简单说一下scrapy的框架结构

    Python爬虫基础之简单说一下scrapy的框架结构

    今天给大家带来的是关于Python爬虫的相关知识,文章围绕着scrapy的框架结构展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • pytorch获取模型某一层参数名及参数值方式

    pytorch获取模型某一层参数名及参数值方式

    今天小编就为大家分享一篇pytorch获取模型某一层参数名及参数值方式,具有很好的价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • 解决Python 命令行执行脚本时,提示导入的包找不到的问题

    解决Python 命令行执行脚本时,提示导入的包找不到的问题

    今天小编就为大家分享一篇解决Python 命令行执行脚本时,提示导入的包找不到的问题,具有很好的参考价值,希望对大家有所帮助,一起跟随小编过来看看吧
    2019-01-01
  • Python 列表的清空方式

    Python 列表的清空方式

    今天小编就为大家分享一篇Python 列表的清空方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • python 中Mixin混入类的使用方法详解

    python 中Mixin混入类的使用方法详解

    这篇文章主要介绍了python 中Mixin混入类的使用方法详解,Mixin 混入也可以说是编程模式,并不是什么新的语法,用好混入类可以使自己的代码结构清晰,功能明了,所以以后在设计类时要多考虑使用Mixin混入类的实现方式
    2022-07-07
  • python thrift搭建服务端和客户端测试程序

    python thrift搭建服务端和客户端测试程序

    这篇文章主要为大家详细介绍了python thrift搭建服务端和客户端测试程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • TensorFlow Session会话控制&Variable变量详解

    TensorFlow Session会话控制&Variable变量详解

    今天小编就为大家分享一篇TensorFlow Session会话控制&Variable变量详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • python使用matplotlib绘制折线图的示例代码

    python使用matplotlib绘制折线图的示例代码

    这篇文章主要介绍了python使用matplotlib绘制折线图的示例代码,帮助大家更好的利用matplotlib绘制图形,感兴趣的朋友可以了解下
    2020-09-09

最新评论