首页 文章

共享内存和多处理

提问于
浏览
2

this question及其答案,我想我理解为什么这个python代码:

big_list = [
    {j: 0 for j in range(200000)}
    for i in range(60)
]

def worker():
    for dic in big_list:
        for key in dic:
            pass
        print "."
        time.sleep(0.2)

w = multiprocessing.Process(target=worker)
w.start()

time.sleep(3600)

在执行过程中继续使用越来越多的内存:这是因为子进程将引用计数更新为循环中的共享内存对象,触发"copy-on-write"机制(我可以通过 cat /proc/meminfo | grep MemFree 观察可用内存减少) .

然而,我不明白的是,如果迭代发生在父而不是子进程中,同样的事情会发生:

def worker():
    time.sleep(3600)

w = multiprocessing.Process(target=worker)
w.start()

for dic in big_list:
    for key in dic:
        pass
    print "."
    time.sleep(0.2)

孩子甚至不需要知道 big_list 的存在 .

在这个小例子中,我可以通过在子函数中放置 del big_list 来解决问题,但有时变量引用不像这样可以访问,所以事情变得复杂 .

为什么会发生这种机制,我该如何正确地避免它?

1 回答

  • 2

    fork() 之后,父和子"see"都是相同的地址空间 . 第一次更改公共地址的内存时,写时复制(COW)机制必须克隆包含该地址的页面 . 因此,出于创建COW页面的目的,突变是在儿童中还是在父母中发生并不重要 .

    在您的第二个代码段中,您遗漏了最重要的部分:确切地创建了 big_list 的位置 . 既然你说你可以在孩子身上逃脱 del big_list ,那么在分叉工作流程之前可能存在 big_list . 如果是这样,那么 - 如上所述 - 对于您的症状而言,在父母或孩子中是否修改了 big_list 并不重要 .

    要避免这种情况,请在创建子进程后创建 big_list . 那么它所居住的地址空间将不会被共享 . 或者,在Python 3.4或更高版本中,使用 multiprocessing.set_start_method('spawn') . 然后 fork() 赢了't be used to create child processes, and no address space is shared at all (which is always the case on Windows, which doesn' t有 fork() ) .

相关问题