Python 多进程共享内存:multiprocessing.shared_memory 模块详解

在 Python 中,multiprocessing 模块提供了多进程支持,而多进程编程中,经常会遇到需要多个进程共享数据的需求。为了避免进程之间的高昂数据传输成本,可以使用共享内存机制。在 Python 3.8 版本及之后,multiprocessing.shared_memory 模块提供了更高效的共享内存操作,使得不同进程可以访问同一块内存区域而无需复制数据。

本文将详细介绍 multiprocessing.shared_memory 模块的使用方法,并通过示例展示如何实现多进程之间的共享内存。


1. 什么是共享内存?

共享内存是一种进程间通信(IPC)方式,多个进程可以直接访问一块共享的内存区域。在 Python 中,multiprocessing.shared_memory 模块允许多个进程共享同一内存块,避免了进程间数据传输的复制开销。

在 Python 3.8 之前,multiprocessing 中的共享内存是通过 Value 和 Array 等方式实现的,而 multiprocessing.shared_memory 提供了一种更为高效且灵活的共享内存机制。

2. multiprocessing.shared_memory 模块简介

multiprocessing.shared_memory 模块允许不同进程访问同一块内存区域,具体提供了以下关键类:

  • SharedMemory:这是主要的类,表示一块共享内存区域。你可以通过它创建共享内存、访问共享内存、传递共享内存给其他进程等。
  • ShareableList:这是一个可以在多进程之间共享的列表,内部分配在共享内存中。

3. 如何使用 multiprocessing.shared_memory

3.1 创建和访问共享内存

要使用共享内存,首先需要通过 SharedMemory 类创建一块共享内存,然后可以通过索引方式访问内存中的数据。

示例:创建共享内存并在多个进程中使用
import multiprocessing
from multiprocessing import shared_memory
import numpy as np

def modify_shared_memory(shm_name):
    # 连接到已经存在的共享内存
    existing_shm = shared_memory.SharedMemory(name=shm_name)
    
    # 将共享内存转换为 NumPy 数组
    np_array = np.ndarray((4,), dtype=np.int64, buffer=existing_shm.buf)
    
    # 修改共享内存中的数据
    for i in range(len(np_array)):
        np_array[i] *= 2
    print("Child Process Modified Data:", np_array)

def main():
    # 创建一块共享内存区域,大小为 4 * 8 字节(4 个 int64 类型的元素)
    shm = shared_memory.SharedMemory(create=True, size=4 * 8)
    
    # 将共享内存转换为 NumPy 数组
    np_array = np.ndarray((4,), dtype=np.int64, buffer=shm.buf)
    
    # 初始化数据
    np_array[:] = [1, 2, 3, 4]
    print("Initial Data in Parent Process:", np_array)
    
    # 创建并启动子进程
    p = multiprocessing.Process(target=modify_shared_memory, args=(shm.name,))
    p.start()
    p.join()
    
    # 子进程修改后的共享内存数据
    print("Data in Parent Process After Child Modification:", np_array)
    
    # 清理共享内存
    shm.close()
    shm.unlink()

if __name__ == "__main__":
    main()

代码解析:

  1. 在 main 函数中,通过 shared_memory.SharedMemory(create=True, size=4 * 8) 创建一块大小为 4 * 8 字节的共享内存。这块内存会存储 4 个 int64 类型的数据。
  2. 然后,将这块共享内存转换为一个 numpy 数组,方便进行数据操作。
  3. 在父进程中,初始化共享内存数据为 [1, 2, 3, 4],然后启动一个子进程。
  4. 子进程通过 shared_memory.SharedMemory(name=shm_name) 获取共享内存的引用,并通过 numpy 数组访问并修改共享内存中的数据。
  5. 最后,父进程打印出修改后的数据,证明共享内存中的数据是可以被多个进程共享和修改的。

输出:

Initial Data in Parent Process: [1 2 3 4]
Child Process Modified Data: [2 4 6 8]
Data in Parent Process After Child Modification: [2 4 6 8]

3.2 共享内存的清理

使用完共享内存后,应该调用 shm.close() 来关闭共享内存,并调用 shm.unlink() 来解除共享内存的连接。

4. 高级用法:ShareableList 和 ShareableDict

除了直接使用 SharedMemorymultiprocessing.shared_memory 还提供了两种高层次的封装,ShareableList和 ShareableDict,它们分别提供了多进程共享列表和字典的功能。

4.1 使用 ShareableList

ShareableList 是一个支持多进程共享的列表,它会将数据存储在共享内存中。

from multiprocessing import shared_memory

def modify_shared_list(shm_name):
    # 连接到现有的共享内存
    existing_shm = shared_memory.ShareableList(name=shm_name)
    
    # 修改共享列表中的数据
    existing_shm[0] = 99
    print("Child Process Modified Data:", existing_shm)

def main():
    # 创建一个支持多进程共享的列表
    shared_list = shared_memory.ShareableList([1, 2, 3, 4])
    
    print("Initial Data in Parent Process:", shared_list)
    
    # 启动子进程修改共享列表
    p = multiprocessing.Process(target=modify_shared_list, args=(shared_list.name,))
    p.start()
    p.join()
    
    print("Data in Parent Process After Child Modification:", shared_list)
    
    # 清理共享内存
    shared_list.shm.close()
    shared_list.shm.unlink()

if __name__ == "__main__":
    main()

输出:

Initial Data in Parent Process: [1, 2, 3, 4]
Child Process Modified Data: [99, 2, 3, 4]
Data in Parent Process After Child Modification: [99, 2, 3, 4]

4.2 使用 ShareableDict

ShareableDict 是一个可以在多个进程之间共享的字典。

from multiprocessing import shared_memory

def modify_shared_dict(shm_name):
    # 连接到现有的共享字典
    existing_shm = shared_memory.ShareableDict(name=shm_name)
    
    # 修改共享字典中的数据
    existing_shm['key1'] = 'value1'
    print("Child Process Modified Data:", existing_shm)

def main():
    # 创建一个支持多进程共享的字典
    shared_dict = shared_memory.ShareableDict({'key': 'initial_value'})
    
    print("Initial Data in Parent Process:", shared_dict)
    
    # 启动子进程修改共享字典
    p = multiprocessing.Process(target=modify_shared_dict, args=(shared_dict.name,))
    p.start()
    p.join()
    
    print("Data in Parent Process After Child Modification:", shared_dict)
    
    # 清理共享内存
    shared_dict.shm.close()
    shared_dict.shm.unlink()

if __name__ == "__main__":
    main()

输出:

Initial Data in Parent Process: {'key': 'initial_value'}
Child Process Modified Data: {'key': 'initial_value', 'key1': 'value1'}
Data in Parent Process After Child Modification: {'key': 'initial_value', 'key1': 'value1'}

5. 总结

  • multiprocessing.shared_memory 模块使得多个进程可以直接访问共享内存,提高了多进程间数据共享的效率,避免了传统进程间通信方式(如管道、队列)的数据复制开销。
  • 使用 SharedMemory 类可以创建共享内存,而 ShareableList 和 ShareableDict 类提供了对共享列表和字典的高层次封装。
  • 共享内存的清理非常重要,使用完毕后需要调用 shm.close() 和 shm.unlink() 来释放资源。

通过这种方式,可以更高效地在多进程中进行数据共享,尤其适用于需要频繁访问和修改共享数据的场景。