Python多进程共享内存

=================================================================
Python 多进程共享内存:高效数据交换方案
=================================================================

多进程间数据传递通常通过序列化(pickle)进行,开销较大。
共享内存允许多个进程直接访问同一块内存区域,实现零拷贝
数据交换。本文详解各种共享内存技术。

=================================================================
一、multiprocessing.Value / Array:基础共享内存
=================================================================

import multiprocessing as mp
import time
import numpy as np

"""
Value 和 Array 使用 ctypes 在共享内存中分配数据。
内部使用 mmap 或系统共享内存 API。
"""

def value_array_demo():
"""使用 Value 和 Array 在进程间共享数据"""

# 创建共享整数(使用 'i' 类型码)
shared_counter = mp.Value('i', 0)

# 创建共享双精度浮点数数组(10 个元素)
shared_array = mp.Array('d', [0.0] * 10)

def worker(counter, arr, worker_id: int):
"""工作进程:修改共享数据"""
# 修改共享值
with counter.get_lock(): # Value 自带锁
counter.value += 1
print(f"工作进程 {worker_id}: counter = {counter.value}")

# 修改共享数组
for i in range(len(arr)):
arr[i] += worker_id * 10

processes = []
for i in range(4):
p = mp.Process(target=worker, args=(
shared_counter, shared_array, i
))
p.start()
processes.append(p)

for p in processes:
p.join()

print(f"最终 counter: {shared_counter.value}")
print(f"最终 array: {list(shared_array)}")

# 常用类型码:
# 'c': char, 'b': signed char, 'B': unsigned char
# 'h': short, 'H': unsigned short
# 'i': int, 'I': unsigned int
# 'l': long, 'L': unsigned long
# 'f': float, 'd': double

=================================================================
二、multiprocessing.shared_memory(Python 3.8+)
=================================================================

"""
shared_memory 模块提供了更底层和灵活的共享内存接口。
支持任意大小的内存块,可以存储任何二进制数据。
"""

from multiprocessing import shared_memory

def shared_memory_basic():
"""使用 SharedMemory 在进程间共享数据"""

# 创建 100 字节的共享内存块
shm = shared_memory.SharedMemory(
name="my_shared_mem", # 命名(None 则自动生成)
create=True,
size=100, # 字节数
)

try:
# 通过缓冲区接口写入数据
buffer = shm.buf # memoryview 对象
buffer[:5] = b"Hello"
buffer[5:13] = b" World! "
# 写入整数(4 字节,小端序)
buffer[13:17] = (42).to_bytes(4, 'little')

# 从另一个进程读取(在另一个进程中通过名称打开)
def reader_process(shm_name: str):
"""读取共享内存的子进程"""
existing_shm = shared_memory.SharedMemory(
name=shm_name, create=False
)
data = existing_shm.buf[:17].tobytes()
print(f"读取到: {data}")
text = data[:13].decode()
number = int.from_bytes(data[13:17], 'little')
print(f"文本: {text}, 数字: {number}")
existing_shm.close()

p = mp.Process(target=reader_process, args=(shm.name,))
p.start()
p.join()

print(f"原始数据: {shm.buf[:17].tobytes()}")
finally:
# 清理共享内存
shm.close()
shm.unlink() # 删除共享内存块

=================================================================
三、SharedMemoryManager:安全管理器
=================================================================

from multiprocessing.managers import SharedMemoryManager

def shared_memory_manager_demo():
"""
SharedMemoryManager 自动管理共享内存的生命周期。
所有创建的共享内存在管理器退出时自动清理。
"""
with SharedMemoryManager() as smm:
# 创建共享内存段
shm_a = smm.SharedMemory(size=64)
shm_b = smm.SharedMemory(size=128)

print(f"共享内存名: {shm_a.name}")
print(f"共享内存大小: {shm_a.size} 字节")

# 使用 ShareableList 存储 Python 对象
sl = smm.ShareableList(
["hello", 42, 3.14, b"bytes", None]
)
print(f"共享列表: {sl}")
print(f"sl[0] = {sl[0]}, sl[1] = {sl[1]}")

# 在子进程中读写
def process_func(name: str):
"""子进程访问共享内存"""
shm = shared_memory.SharedMemory(name=name)
shm.buf[:6] = b"World!"
shm.close()

p = mp.Process(target=process_func, args=(shm_a.name,))
p.start()
p.join()

print(f"子进程写入后: {shm_a.buf[:6].tobytes()}")

# 管理器退出时自动清理所有共享内存

=================================================================
四、共享内存 vs Queue 性能对比
=================================================================

def shared_memory_vs_queue():
"""
对比共享内存和 Queue 在大数据量传输时的性能。
共享内存避免了序列化/反序列化的开销。
"""
DATA_SIZE = 1_000_000 # 100 万个整数
data = list(range(DATA_SIZE))

# === 使用 Queue 传输 ===
start = time.perf_counter()
q = mp.Queue()

def queue_sender(q, data):
q.put(data)

def queue_receiver(q):
_ = q.get()

p1 = mp.Process(target=queue_sender, args=(q, data))
p2 = mp.Process(target=queue_receiver, args=(q,))

p1.start()
p2.start()
p1.join()
p2.join()
queue_time = time.perf_counter() - start

# === 使用共享内存传输 ===
start = time.perf_counter()
import array

# 将数据写入共享内存
arr = array.array('i', data)
shm = shared_memory.SharedMemory(
create=True, size=len(arr) * arr.itemsize
)
shm.buf[:len(arr) * arr.itemsize] = bytes(arr)

def shm_receiver(shm_name, size):
ex_shm = shared_memory.SharedMemory(name=shm_name)
result = array.array('i')
result.frombytes(ex_shm.buf[:size])
_ = result.tolist()
ex_shm.close()

p = mp.Process(target=shm_receiver, args=(shm.name, len(arr) * arr.itemsize))
p.start()
p.join()
shm_time = time.perf_counter() - start

shm.close()
shm.unlink()

print(f"=== 性能对比({DATA_SIZE} 个整数) ===")
print(f"Queue 传输: {queue_time:.3f}s")
print(f"共享内存: {shm_time:.3f}s")
print(f"加速比: {queue_time / shm_time:.1f}x")
# 数据量越大,共享内存的优势越明显

=================================================================
五、在共享内存中使用 NumPy
=================================================================

def shared_memory_numpy():
"""
在共享内存中创建 numpy 数组。
多个进程可以零拷贝地操作同一份数据。
"""
with SharedMemoryManager() as smm:
# 创建共享内存
shape = (1000, 1000) # 1000x1000 的矩阵
dtype = np.float64
# 计算需要的字节数
nbytes = np.prod(shape) * np.dtype(dtype).itemsize
shm = smm.SharedMemory(size=nbytes)

# 创建 numpy 数组指向共享内存
shared_array = np.ndarray(
shape, dtype=dtype, buffer=shm.buf
)

# 初始化数据
shared_array[:] = np.random.rand(*shape)
print(f"共享数组形状: {shared_array.shape}")
print(f"共享数组大小: {nbytes / 1024 / 1024:.2f} MB")

def compute_sum(shm_name, shape, dtype):
"""在子进程中计算数组和"""
ex_shm = shared_memory.SharedMemory(name=shm_name)
arr = np.ndarray(shape, dtype=dtype, buffer=ex_shm.buf)
total = np.sum(arr)
print(f"子进程计算结果: {total:.2f}")
ex_shm.close()

p = mp.Process(
target=compute_sum,
args=(shm.name, shape, dtype),
)
p.start()
p.join()

print(f"主进程验证: {np.sum(shared_array):.2f}")

=================================================================
六、数据同步:避免竞态条件
=================================================================

"""
共享内存本身不提供同步机制。当多个进程同时修改同一块
内存时,需要使用 Lock 来避免竞态条件。
"""

def race_condition_demo():
"""演示共享内存的竞态条件及解决方案"""
shm = shared_memory.SharedMemory(
create=True, size=8 # 存储一个 64 位整数
)
lock = mp.Lock() # 进程间锁
NUM_INCREMENTS = 10000

# 初始化计数器
shm.buf[:8] = (0).to_bytes(8, 'little')

def unsafe_increment(shm_name):
"""不安全的递增(没有锁)"""
ex_shm = shared_memory.SharedMemory(name=shm_name)
for _ in range(NUM_INCREMENTS):
# 读取-修改-写入(非原子操作)
val = int.from_bytes(ex_shm.buf[:8], 'little')
val += 1
ex_shm.buf[:8] = val.to_bytes(8, 'little')
ex_shm.close()

def safe_increment(shm_name, lock):
"""安全的递增(使用锁)"""
ex_shm = shared_memory.SharedMemory(name=shm_name)
for _ in range(NUM_INCREMENTS):
with lock:
val = int.from_bytes(ex_shm.buf[:8], 'little')
val += 1
ex_shm.buf[:8] = val.to_bytes(8, 'little')
ex_shm.close()

# 启动多个进程(不使用锁)
procs = [
mp.Process(target=unsafe_increment, args=(shm.name,))
for _ in range(4)
]
for p in procs: p.start()
for p in procs: p.join()

result = int.from_bytes(shm.buf[:8], 'little')
expected = NUM_INCREMENTS * 4
print(f"无锁: 实际={result}, 期望={expected}, 误差={expected-result}")

# 重置并测试有锁版本
shm.buf[:8] = (0).to_bytes(8, 'little')
procs = [
mp.Process(target=safe_increment, args=(shm.name, lock))
for _ in range(4)
]
for p in procs: p.start()
for p in procs: p.join()

result = int.from_bytes(shm.buf[:8], 'little')
print(f"有锁: 实际={result}, 期望={expected}")

shm.close()
shm.unlink()

=================================================================
七、总结
=================================================================

# 1. Value/Array:简单的 ctypes 共享内存,自带锁
# 2. SharedMemory(3.8+):灵活的低层共享内存 API
# 3. SharedMemoryManager:自动管理共享内存生命周期
# 4. 大数据量时共享内存比 Queue 快(避免序列化)
# 5. numpy 数组可零拷贝映射到共享内存
# 6. 共享内存需要手动同步(Lock)避免竞态
# 7. 使用完毕后必须 unlink() 清理系统资源
# 8. SharedMemoryManager 的 ShareableList 可存混合类型

if __name__ == "__main__":
shared_memory_vs_queue()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值