在进程间共享数据而无需实际移动它

时间:2012-11-15 11:01:47

标签: python multiprocessing shared-memory

我有一份工作,我可以完成很多单独的任务。对于每个任务,我需要下载一些数据,处理它然后再次上传。

我正在使用多处理池进行处理。

我有几个我不确定的问题。

首先,数据大概可达20MB,理想情况下,我希望将其转移到子工作进程,而无需在内存中进行物理移动,并将结果数据返回到父进程,而无需移动它。由于我不确定某些工具是如何工作的,我不知道我是否可以将数据作为参数传递给池apply_async(根据我的理解,它将对象序列化然后它们是一旦到达子流程再次创建?),或者我是否应该使用多处理Queuemmap?或其他什么?

我查看了ctypes objects但是根据我的理解,只有在可以共享流程分支时创建池时定义的对象?这对我没有好处,因为我将继续有新的数据进入,我需要分享。

我不应该担心的一件事是对数据的任何并发访问,所以我不需要任何类型的锁定。这是因为处理只会在下载数据后开始,上传也只会在生成输出数据后开始。

我遇到的另一个问题是,有时候进入的任务可能会飙升,因此我会比子进程处理它更快地下载任务数据。因此,我下载的数据比我完成任务和处理数据更快,而python因内存不足而死亡。当内存几乎已满/在作业管道中有太多数据时,在下载阶段阻止任务的好方法是什么? 我通过使用数据字节数来考虑某种类型的“ref”计数,因此我可以限制下载和上载之间的数据量,并且只有在数量低于某个阈值时才下载。虽然我担心孩子有时可能会失败,但我永远不会把它带走的数据拿走了。有没有一种很好的方法来实现这种目标?

3 个答案:

答案 0 :(得分:2)

以下是多处理documentation的简单示例:

来自多处理导入过程,值,数组

def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    num = Value('d', 0.0)
    arr = Array('i', range(10))

    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()

    print num.value
    print arr[:]

请注意,numarr是共享对象。这是你在找什么?

答案 1 :(得分:2)

(这是我之前回答的讨论结果)

您是否尝试过POSH

此示例显示可以将元素附加到可变列表,这可能是您想要的(从documentation复制):

import posh

l = posh.share(range(3))
if posh.fork():
    #parent process
    l.append(3)
    posh.waitall()
else:
    # child process
    l.append(4)
    posh.exit(0)
print l

-- Output --
[0, 1, 2, 3, 4]
  -- OR --
[0, 1, 2, 4, 3]

答案 2 :(得分:0)

我一起破坏了这个,因为无论如何我需要为自己解决这个问题。在多处理或线程方面,我绝不是非常有成就,但至少它可行。也许它可以以更智能的方式完成,我无法弄清楚如何使用非原始Array类型附带的锁。也许有人会建议改进评论。

from multiprocessing import Process, Event
from multiprocessing.sharedctypes import RawArray

def modify(s, task_event, result_event):
    for i in range(4):
        print "Worker: waiting for task"
        task_event.wait()
        task_event.clear()
        print "Worker: got task"
        s.value = s.value.upper()

        result_event.set()

if __name__ == '__main__':
    data_list = ("Data", "More data", "oh look, data!", "Captain Pickard")
    task_event = Event()
    result_event = Event()
    s = RawArray('c', "X" * max(map(len, data_list)))
    p = Process(target=modify, args=(s, task_event, result_event))
    p.start()
    for data in data_list:
        s.value = data
        task_event.set()
        print "Sent new task. Waiting for results."
        result_event.wait()
        result_event.clear()
        print "Got result: {0}".format(s.value)
    p.join()

在此示例中,data_list是事先定义的,但不一定是。我从该列表中获得的唯一信息是最长字符串的长度。只要你有一些长度的实际上限,就没问题了。

以下是该计划的输出:

Sent new task. Waiting for results.
Worker: waiting for task
Worker: got task
Worker: waiting for task
Got result: DATA
Sent new task. Waiting for results.
Worker: got task
Worker: waiting for task
Got result: MORE DATA
Sent new task. Waiting for results.
Worker: got task
Worker: waiting for task
Got result: OH LOOK, DATA!
Sent new task. Waiting for results.
Worker: got task
Got result: CAPTAIN PICKARD

正如你所看到的,btel确实提供了解决方案,但问题在于保持两个进程彼此保持同步,这样工作人员只有在任务准备就绪时才开始处理新任务,所以主要过程在完成之前不会读取结果。