python多进程并发和数据共享(使用队列、大数组通信)

建议和原则:

1. 进程间(甚至是机器间)数据共享用Manager,数据交换用 Pipe或Queue

2. 进程之间默认是不能共享全局变量的 (子进程不能改变主进程中全局变量的值)。

3. 共享全局变量:需要用(multiprocessing.Value("d",10.0),数值)(multiprocessing.Array("i",[1,2,3,4,5]),数组)(multiprocessing.Manager().dict(),字典)(multiprocessing.Manager().list(range(5)),列表)。

4. 进程间通信(进程之间传递数据)的方式有:队列(multiprocessing.Queue(),单向通信),管道( multiprocessing.Pipe() ,双向通信)等。

 

一些思路:

一、少量数据通信:通过subprocess.Popen()的stdin, stdout输入输出。或者通过它的communicate

详细介绍subprocess的api:https://docs.python.org/zh-cn/3/library/subprocess.html#subprocess.Popen.communicate

方法1:stdin, stdout: https://blog.csdn.net/nescafe1111/article/details/15028739

方法2:communicate:https://www.liaoxuefeng.com/wiki/1016959663602400/1017628290184064

二、大数组传递:全局变量?共享内存?queue?pipe?

方法1:全局变量:

方法2:共享内存:sharedmem,http://www.voidcn.com/article/p-wfnoaoho-bsx.html

方法3:queue: https://blog.csdn.net/brucewong0516/article/details/85796073

方法4:pipe: https://blog.csdn.net/mixintu/article/details/102073990

方法5:先变成一维,再通过multiprocessing.Array()传递,接收后再变为多维

 

实测:

1. 进程间共享或者访问处理少量数据,用Manager的list或者dict是可以的。

2. 而大数组之类的数据交换和访问不要用Manager,否则速度会很慢!!!!!(猜测它们底层还是用RawArray等函数实现一维和多维的转换的,所以速度不行!!!)这种情况下还是得用Multiprocessing.Pipe或者Multiprocessing.Queue

 

需求:

1,运行主函数,并行开启子进程1和子进程2

2,实时接收子进程1的输出

3,实时监听子进程2的忙闲状态,在子进程2空闲时,将子进程1的输出传给子进程2处理;

在子进程2忙时,暂存缓冲,等子进程2空闲时再传递给它处理

4,最终主函数的输出顺序按照 子进程1的输出传递给子进程2的顺序 进行输出

import multiprocessing as mp
from multiprocessing import Process, Queue

time_cost=[]

# 子进程1,producer
def proc1(input_queue):
input_queue.put({k:v})

# 子进程2,consumer
def proc2(input_queue):
global time_cost
while True:
'''
block=True, 阻塞方式运行。队列数据满或者为空时,让生产或消费者等待,等timeout时间到达后抛出异常
block=False,非阻塞,队列数据满或为空时,直接抛出异常
默认阻塞方式,timeout=None. timeout为None,无限阻塞(允许阻塞的事件没有限制)
'''
try:
t1 = time.time() * 1000 # 单位:毫秒

# input_dict =input_q.get(block=True, timeout=2) # 阻塞方式运行,proc1和proc2同时只能运行1个,交替运行,相当于还是串行。
input_dict = input_q.get(block=False) # 非阻塞方式运行,proc1和proc2同时运行,consumer始终保持运行,producer一产生结果放入到队列里,马上就进行处理。真正的并行
[(k, v)] = input_dict.items()

if k == 'False':
break

print('proc2 result!')

t2 = time.time()-t1
time_cost.append(t2)

execpt:
pass
print('avg. time cost:', sum(time_cost) / len(time_cost))

# 主函数
if __name__ == '__main__':
t_start = time.time() * 1000 # 程序启动
print('start:', t_start)

input_q = mp.Queue()
s_proc1 = Process(target=proc1, args=(input_q, ))
s_proc2 = Process(target=proc2, args=(input_q, ))

s_proc1.start()
s_proc2.start()

s_proc1.join() # 强制执行完毕
s_proc2.join()

t_stop = time.time() * 1000 # 程序结束
print('end:', t_stop - t_start)
其他:

# Poll方法查看子进程的状态:
0 正常结束
1 sleep
2 子进程不存在
-15 kill
None 在运行

# 经实测,此方法不能成为proc2中while循环的判断条件
使用大数组(如果数组较大的话,不建议使用此方法,因为速度慢!):

>>> import numpy as np

SHAPE=(3,3,3)

# 要保存的单个数据元素
>>> d = np.array([[[1,2,3], [3,4,5], [4,5,6], [1,2,3], [3,4,5], [4,5,6], [1,2,3], [3,4,5], [4,5,6],]])
>>> d = d.reshape((SHAPE))

# 进程间传递数据的载体:大数组
>>> send_arr = []
>>> send_arr.append(d)
>>> send_arr.append(d)

# 查看传递的内容
>>> send_arr
[array([[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]],

[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]],

[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]]]), array([[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]],

[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]],

[[1, 2, 3],
[3, 4, 5],
[4, 5, 6]]])]

# 扁平化之后才能传递。这一步比较耗时!! 其他扁平化方法未测试:squeeze, reshape(-1)..
>>> e_share = mp.RawArray('d', send_arr.ravel())

# 接收到之后解包
>>> e_new = np.frombuffer(e_share, dtype=np.double)
>>> k = e_new.reshape((2,9,3)) # 大数组包含的元素个数,单个元素包含的子元素个数,单个元素的列数
>>> recv_arr = []
>>> for i in k:
... i = np.array(i)
... i = i.reshape((3,3,3))
... recv_arr.append(i)
...

# 查看接收到的内容
>>> recv_arr
[array([[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]],

[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]],

[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]]]), array([[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]],

[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]],

[[1., 2., 3.],
[3., 4., 5.],
[4., 5., 6.]]])]

---------------------------------------------------------------------------------------------------------

整理一下,有机会应该每个都实现一遍。网上的例程最大的问题就是逻辑和数据假设的太简单,包括运行环境。导致一放到实际中就不能用。

 

完整的介绍python多进程:https://www.cnblogs.com/kaituorensheng/p/4445418.html

完整的介绍长时间运行的多个进程之间的相互通信:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/

如何优雅地停止多进程:https://blog.csdn.net/qxqxqzzz/article/details/105642875

 

其他参考:

进程间通信(IPC): https://cloud.tencent.com/developer/article/1496658
参考:https://blog.csdn.net/houyanhua1/article/details/78236514

 

其他致谢:

https://blog.csdn.net/faihung/article/details/90516180

posted @ 2022-06-02 12:42  泡泡茶壶i  阅读(2714)  评论(0编辑  收藏  举报