我已经编写了一些代码,我想通过它来测试多处理的一些特定问题:
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等待所有子进程完成,但是父进程的输出似乎总是与子进程的混乱,为什么?谢谢,如果有人能为我解释这些。
答案 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
或其他同步对象。)