共享内存并加入python中的多处理问题

时间:2015-05-17 09:26:21

标签: python multiprocessing shared-memory python-multiprocessing

我已经编写了一些代码,我想通过它来测试多处理的一些特定问题:

import multiprocessing as mp
import sys
z = 10
file = open("test_file")
file2 = open("test_multiprocess", "w")
arr = []

def func(obj, idx):
    print("pid[%d] [%s]" % (idx,str(id(obj))))
    if idx == 1:
        obj += 3
    elif idx == 2:
        obj = open("test_multiprocess")
    elif idx == 3:
        obj = open("test_multiprocess_%d" % idx, "w")
    elif idx == 4:
        obj.append(idx)
    print("pid[%d] after changing [%s]" % (idx, str(id(obj))))
    sys.stdout.flush()

if __name__ == "__main__":
    data = {1:z, 2:file, 3:file2, 4:arr}
    p = []
    print("original id is [%s] [%s] [%s] [%s]" % (str(id(data[1])), str(id(data[2])), str(id(data[3])), str(id(data[4]))))
    print("==============================================================")
    for i in range(1, 5):
        p.append(mp.Process(target=func, args=(data[i], i)))
        p[len(p)-1].start()

    for i in range(i, len(p)):
        p[i].join()

    sys.stdout.flush()
    print("==============================================================")
    print("after process id is [%s] [%s] [%s] [%s]" % (str(id(data[1])), str(id(data[2])), str(id(data[3])), str(id(data[4]))))

然而,当我运行这个文件时,我发现了一些奇怪的现象。一个特定的输出如下:

original id is [6330144] [140069930330512] [140069930330992] [140069873535384]
==============================================================
pid[1] [6330144]
pid[1] after changing [6330072]
pid[2] [140069930330512]
pid[2] after changing [140069864561880]
pid[3] [140069930330992]
pid[3] after changing [140069864561880]
==============================================================
after process id is [6330144] [140069930330512] [140069930330992] [140069873535384]
pid[4] [140069873535384]
pid[4] after changing [140069873535384]

首先,当将数据[i]传递给子进程时,id(data [i])根本不会改变,但是AFAIK python fork()由于ref-count的改变而是copy-on-access。其次,假设它是python中的copy-on-write,当obj在subprocess中被修改时,int / File类型对象确实改变了它的id但是对于类型列表不是这样,因为我们可以看到它的id永远不会改变ID。最后但并非最不重要的是,我使用join等待所有子进程完成,但是父进程的输出似乎总是与子进程的混乱,为什么?谢谢,如果有人能为我解释这些。

1 个答案:

答案 0 :(得分:0)

这些都与多处理无关。您可以运行完全相同的测试,只需在进程中调用相同的函数,您将获得相同的结果。

  

首先,当将数据[i]传递给子进程时,id(data [i])根本不会改变

那是因为你不会在任何地方改变data[i]

当您将data[i]作为参数传递时,obj参数不会成为变量data[i]的引用,它将成为对相同值的引用那就是那个变量。

以后执行obj = …时,不会以任何方式影响价值;它只会使obj引用不同的值。

如果你想改变data,你必须自己传递data(并且,大概是i);然后该函数可以data[i] =

  

其次,假设它是python中的copy-on-write,当在subprocess中修改obj时,int /文件类型对象确实改变了它的id但是对于类型列表不是这样

不。差异与类型无关,或与写时复制无关。 obj = …永远不会影响原始对象。无论是int还是list都无关紧要。

您看到不同行为的原因并不是类型不同,而是您拥有不同的代码。您没有obj = …,而是obj.append(…)。这是对象的一种方法,可以就地改变它。

(如果你对obj += 3感到好奇,那一个有点棘手。增强赋值可以就地改变值以及为变量赋值,或者它可能只是为变量分配一个新值。它取决于值的类型。通常,像list这样的可变类型将就地变异并将self赋给变量;不可变类型如{ {1}}当然不会就地变异,他们总是会分配一个新值。)

我的猜测是你来自C ++这样的语言,其中变量实际上是值所在的内存位置,赋值是一种变异方法(通常复制值),而显式引用是对变量的引用,而不是值。如果是这样,可能有助于将Python中的每个变量都考虑为int,而不是std::shared_ptr<boost::any>

  

最后但并非最不重要的是,我使用join等待所有子进程完成,但是父进程的输出似乎总是与子进程的混乱,为什么?

因为实际上等待所有子进程完成。这一行:

boost::any&

...遍历for i in range(i, len(p)): ,这是空的,因此您不会range(5, 4)任何内容。

这是直接在集合上进行迭代的众多原因之一:

join

如果你这样做,就没有地方可以插入难以调试的计数错误。

与此同时,即使事实证明与您的实际代码完全无关,但如果您对继承的内容和内容感兴趣,请阅读start methods及相关的{{3} }。但简单地说:如果您使用for proc in p: proc.join() 方法(非Windows系统上的默认方法),您的全局变量将直接共享;如果您使用的是fork(Windows上的默认设置),则会从源代码重新构建它们。但是如果你真的需要共享变量(在大多数情况下你真的不想这样做),你几乎不应该依赖spawn行为,即使你只是在Unix上运行;使用显式programming recommendations。 (另外,如果你真的需要共享任何可以被多个进程变异的东西,你真的需要一个fork或其他同步对象。)