简体   繁体   English

共享内存和python中多处理的连接问题

[英]shared memory and join issues with multiprocessing in python

I have written some codes as below, through which I wanted to test some specific problems with 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]))))

However, when I ran this file, I found some weird phenomena. 但是,当我运行此文件时,我发现了一些奇怪的现象。 One particular output is as below: 一种特定的输出如下:

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]

First of all, when passing data[i] to subprocess, id(data[i]) does not change at all, but AFAIK python fork() is copy-on-accessing because of ref-count changing. 首先,当将data [i]传递给子进程时,id(data [i])完全没有变化,但是AFAIK python fork()会由于引用计数的更改而在访问时复制。 Secondly, suppose it is copy-on-write in python, when obj is modified in subprocess, int/File type object does change its id but this is not true with type list as we can see its id never changes even compared with the original id. 其次,假设它在python中是写时复制的,当在子进程中修改obj时,int / File类型的对象确实更改了其id,但是对于类型列表而言,这是不正确的,因为我们可以看到,即使与原始对象相比,其id也不会改变ID。 Last but not least, I use join to wait for all subprocesses to complete, but output of parent process seems to always be messed up with subprocess ones, why? 最后但并非最不重要的一点是,我使用join等待所有子流程完成,但是父流程的输出似乎总是与子流程混淆,为什么呢? Thanks if any one could explain these for me. 多谢有人能为我解释这些。

None of this has anything to do with multiprocessing. 这些都与多处理无关。 You can run the exact same test just calling the same function in-process and you'll get the same results. 您只需在进程中调用相同的函数即可运行完全相同的测试,您将获得相同的结果。


First of all, when passing data[i] to subprocess, id(data[i]) does not change at all 首先,将data [i]传递给子流程时,id(data [i])完全没有变化

That's because you don't change data[i] anywhere. 那是因为您不会在任何地方更改data[i]

When you pass data[i] as an argument, the obj parameter doesn't become a reference to the variable data[i] , it becomes a reference to the same value that's in that variable. 当您将data[i]作为参数传递时, obj参数不会成为对变量data[i]的引用,它将成为对该变量中相同的引用。

When you later do obj = … , that doesn't affect the value in any way; 当您以后执行obj = … ,那不会以任何方式影响该值; it just makes obj refer to a different value instead. 它只是使obj引用不同的值。

If you want to mutate data , you have to pass data itself (and, presumably, i ); 如果要变异data ,则必须传递data本身(大概是i ); then the function can do data[i] = . 然后该函数可以执行data[i] =


Secondly, suppose it is copy-on-write in python, when obj is modified in subprocess, int/File type object does change its id but this is not true with type list 其次,假设它在python中是写时复制的,当在子进程中修改obj时,int / File类型对象的确会更改其id,但对于类型列表则不正确

Nope. 不。 The difference has nothing to do with the types, or with copy-on-write. 区别与类型或写时复制无关。 obj = … never affects the original object. obj = …永远不会影响原始对象。 It doesn't matter whether it's an int or a list . int还是list都没关系。

The reason you see different behavior isn't that the type is different, it's that you have different code. 您看到不同行为的原因不是类型不同,而是代码不同。 You don't do obj = … , you do obj.append(…) . 您不执行obj = … ,而是执行obj.append(…) That's a method on the object, which mutates it in-place. 这是对象上的一种方法,可以就地对其进行突变。

(If you're curious about obj += 3 , that one is a bit trickier. Augmented assignment may mutate the value in-place as well as assigning a new value to the variable, or it may just assign a new value to the variable. It's up to the value's type. Generally, mutable types like list will mutate in-place and assign self back to the variable; immutable types like int will of course never mutate in-place, they'll always assign a new value.) (如果您对obj += 3感到好奇,那会有些棘手。增强的分配可能会就地改变值,也可能会为变量分配新值,或者可能只是为变量分配新值通常由值的类型决定。像list这样的可变类型通常会在原处发生变异,并将self赋值给变量;像int这样的不可变类型当然不会在原处发生变异,它们总是会分配一个新值。)


My guess is that you're coming from a language like C++, where variables are actually memory locations where the values live, assignment is a mutating method (which normally copies values), and explicit references are references to variables, not to values. 我的猜测是,您来自C ++这样的语言,其中变量实际上是值所在的内存位置,赋值是一种变异方法(通常会复制值),显式引用是对变量的引用,而不是对值的引用。 If so, it may help to think of every variable in Python a std::shared_ptr<boost::any> , not a boost::any& . 如果是这样,可能有助于将Python中的每个变量都认为是std::shared_ptr<boost::any> ,而不是boost::any&


Last but not least, I use join to wait for all subprocesses to complete, but output of parent process seems to always be messed up with subprocess ones, why? 最后但并非最不重要的一点是,我使用join等待所有子流程完成,但是父流程的输出似乎总是与子流程混淆,为什么呢?

Because you don't actually wait for all subprocesses to complete. 因为你实际上并没有等待所有的子进程来完成。 This line: 这行:

for i in range(i, len(p)):

… iterates over range(5, 4) , which is empty, so you don't join anything. …在range(5, 4) 5,4)上迭代,该范围为空,因此您不join任何内容。

This is one of the many reasons it's better to iterate directly over collections: 这是直接遍历集合的许多原因之一:

for proc in p:
    proc.join()

If you do it that way, there's no place to insert a hard-to-debug counting error. 如果这样做,就没有地方插入难以调试的计数错误。


Meanwhile, even though it turns out to be completely irrelevant to your actual code, if you're interested in what does and doesn't get inherited and how, read about start methods and the related programming recommendations . 同时,即使事实证明它与您的实际代码完全无关,如果您对继承和不继承以及如何继承感兴趣,请阅读启动方法和相关的编程建议 But briefly: if you're using the fork method (the default on non-Windows systems), your globals are shared directly; 但简单来说:如果您使用fork方法(非Windows系统上的默认方法),则直接共享全局变量。 if you're using spawn (the default on Windows), they're re-constructed from the source. 如果您使用的是spawn (Windows上的默认设置),则会从源代码中重新构建它们。 But if you really need to share variables (which you really don't want to do anyway in most cases), you should almost never rely on the fork behavior, even if you're only going to run on Unix; 但是,如果您确实需要共享变量(在大多数情况下,您实际上确实不想这样做),则即使您只打算在Unix上运行,也几乎不应依赖fork行为。 use explicit shared memory . 使用显式共享内存 (Also, if you really need to share anything that can be mutated by more than one process, you really need a Lock or other synchronization object.) (此外,如果您确实需要共享可以由多个进程更改的任何内容,则确实需要一个Lock或其他同步对象。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM