multiprocessing神坑之进程通信

并发

python的并发已经被诟病许久了
GIL锁导致线程级并发争用一个CPU
计算密集型任务在多线程的模式下根本做不到真正意义上的并发
导致python的多线程只适用于非计算密集型任务类似I/O等

避开这个劣势一般有两种策略
其一是使用multiprocessing将线程级转到进程级
其二便是使用C语言

multiprocessing的进程通信方式

multiprocessing开启一个子进程,两者传参的通信方式是使用pickle将参数序列化

pickle是一种python自带的对象序列化方法,类似json
不同之处

  1. pickle是python专有,json是通用型序列化方法
  2. pickle编码方式是二进制,便于I/O以及传输,而json可读性较高I/O方面速度较慢
  3. json只能序列化python中的部分对象,而pickle可以序列化大多数

在python中使用进程并发时隐藏了这些细节
但实际中他是以pickle进行传输数据
这就带来了下面的问题

multiprocessing的进程通信限制

struct.error: ‘i’ format requires -2147483648 <= number <= 2147483647

这是我将一个比较大的矩阵作为参数进行传输时带来的问题

其实一个空间占用极大的对象理当不该作为参数传输
但是进程不像线程没有共享内存的空间诶,写细了很麻烦
再次diss一下Python的并发

这个是触发了多进程中pickle中序列化大小的限制
对象过大无法被序列化 = =

如果一些特殊条件导致不方便直接在硬盘上I/O
还有什么方法给一个进程传送存储量大的数据呢

multiprocessing的进程的共享对象

进程的原则本是尽量避免数据共享…
但有些情况下又不好避免
理当当断则断

multiprocessing中实现了很多用于进程间数据共享的对象
如Queue, Array, Value, NameSpace等等

这些对象共享数据的方式是用过创建一个服务进程
然后其他进程访问相关数据的某部分时
服务进程将这部分数据序列化传输给该进程

这就达到了分批传输的方式

我们这里介绍一个Manager的对象管理方式

创建数据server进程以及给worker进程分配任务

with Manager() as manager :
   dataset = manager.list()
   dataset.append(matrix)
   …
   pool.apply_async(worker,args=( dataset, other para))

worker进程中使用数据

def worker(dataset, other para):
   matrix1 = dataset[0]
   matrix2 = dataset[1]
    …

这与一般的list不同之处在于
manager的list对象的数据访问是从server进程上访问的
dataset.append(matrix) 通过这行代码将数据分批放入server进程中
再在worker进程中 matrix1 = dataset[0] 分批取出

即可避免了过大的数据无法序列化的问题