尝试将大ndarray
放入Queue
的{{1}}时,遇到以下问题:
首先,这是代码:
Process
如果我使用import numpy
import multiprocessing
from ctypes import c_bool
import time
def run(acquisition_running, data_queue):
while acquisition_running.value:
length = 65536
data = numpy.ndarray(length, dtype='float')
data_queue.put(data)
time.sleep(0.1)
if __name__ == '__main__':
acquisition_running = multiprocessing.Value(c_bool)
data_queue = multiprocessing.Queue()
process = multiprocessing.Process(
target=run, args=(acquisition_running, data_queue))
acquisition_running.value = True
process.start()
time.sleep(1)
acquisition_running.value = False
process.join()
print('Finished')
number_items = 0
while not data_queue.empty():
data_item = data_queue.get()
number_items += 1
print(number_items)
左右,一切正常。我通过队列传输了9个项目。
如果我增加到length=10
,则在我的计算机上length=1000
阻止,但功能process.join()
已经完成。我可以使用run()
对该行进行评论,并且会看到,队列中只放置了2个项目,因此显然将数据放入队列的速度非常慢。
我的计划实际上是传输4个ndarray,每个长度为65536.对于process.join()
,这个工作非常快(<1ms)。有没有办法提高流程传输数据的速度?
我在Windows机器上使用Python 3.4,但在Linux上使用Python 3.4我会得到相同的行为。
答案 0 :(得分:7)
&#34;有没有办法提高流程传输数据的速度?&#34;
当然,给出正确的解决问题。目前,您只需填充缓冲区而不会同时清空缓冲区。 恭喜,您刚刚为自己构建了一个所谓的死锁。相应的引用from the documentation是:
请记住,将项目放入队列的进程将等待 在终止之前,直到所有缓冲的项目都由 “馈线”线程到底层管道。
但是,让我们慢慢接近这个。首先,&#34;速度&#34;不是你的问题!我知道您只是在尝试使用Python multiprocessing
。阅读代码时最重要的见解是父母和孩子之间的沟通流程,特别是事件处理并没有多大意义。如果你有一个你想要解决的现实问题,你绝对无法解决它这个方式。如果你没有现实问题,那么在开始编写代码之前,首先需要提出一个好问题;-)。最后,您需要了解操作系统为进程间通信提供的通信原语。
您正在观察的内容的解释:
您的子进程生成大约10 * length * size(float)
个字节的数据(考虑到您的子进程可以执行大约10次迭代,而您的父进程在将acquisition_running
设置为False
之前大约休息1秒)。当您的父进程休眠时,子进程将命名的数据量放入队列中。您需要了解队列是一个复杂的构造。你不需要了解它的每一点。但有一件事确实非常重要:进程间通信的队列显然使用了父进程和子进程之间的某种缓冲区*。缓冲区通常具有有限的尺寸。您正在从子中写入此缓冲区而没有同时从父级中读取它。也就是说,缓冲内容在父母刚睡觉时稳定增长。通过增加length
,您会遇到队列缓冲区已满并且子进程无法再写入它的情况。但是,子进程在写入所有数据之前不能终止。同时,父进程等待子进程终止。
关于细节,缓冲区情况比上述情况稍微复杂一些。您的子进程产生了一个额外的线程,它试图通过管道将缓冲的数据推送到父级。实际上,这个管道的缓冲区是限制实体。它由操作系统定义,至少在Linux上,通常不大于65536字节。
换句话说,基本部分是:在孩子完成尝试写之前,父从管中读 管。在使用管道的每个有意义的场景中,读取和写入以相当同时的方式发生,以便一个进程可以快速响应由另一个过程。你正在做相反的事情:你让你的父母睡觉,因此让它对孩子的输入不敏感,导致死锁情况。
(*)&#34; 当一个进程首先将一个项目放入队列时,启动一个进给者线程,它将对象从缓冲区传输到管道&#34;,来自{{3 }}
答案 1 :(得分:1)
如果你有非常大的数组,你可能只想传递他们的pickle状态 - 或者更好的选择可能是使用multiprocessing.Array
或multiprocessing.sharedctypes.RawArray
来创建一个共享内存数组(对于后者,见http://briansimulator.org/sharing-numpy-arrays-between-processes/)。您必须担心冲突,因为您将拥有一个不受GIL约束的阵列 - 并且需要锁定。但是,您只需要发送数组索引来访问共享数组数据。
答案 2 :(得分:0)
与JPG的优秀答案相结合,您可以做的一件事就是在每个进程之间卸载Queue。
所以这样做:
process.start()
data_item = data_queue.get()
process.join()
虽然这并没有完全复制代码中的行为(数据计数的数量),但你明白了;)
答案 3 :(得分:-2)
将数组/列表转换为str(your_array)
q.put(str(your_array))