【问题标题】:shared memory and join issues with multiprocessing in pythonpython中的多处理共享内存和连接问题
【发布时间】:2015-05-17 09:26:20
【问题描述】:

我写了一些代码如下,我想通过这些代码来测试多处理的一些具体问题:

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]

首先,当将 data[i] 传递给子进程时, id(data[i]) 根本没有改变,但是 AFAIK python fork() 由于引用计数的变化而正在复制访问。其次,假设它在python中是copy-on-write,当obj在子进程中被修改时,int/File类型对象确实改变了它的id,但是对于类型list来说这是不正确的,因为我们可以看到它的id即使与原始相比也不会改变ID。最后但并非最不重要的一点是,我使用 join 等待所有子进程完成,但父进程的输出似乎总是与子进程混淆,为什么?谢谢谁能帮我解释一下。

【问题讨论】:

    标签: python multiprocessing shared-memory python-multiprocessing


    【解决方案1】:

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


    首先,将data[i]传递给子进程时,id(data[i])根本没有变化

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

    当您将data[i] 作为参数传递时,obj 参数不会成为对变量data[i] 的引用,它会成为对该变量中相同 的引用.

    当您稍后执行obj = … 时,这不会以任何方式影响值;它只是让obj 引用不同的值。

    如果你想改变data,你必须传递data本身(并且可能是i);那么函数可以做data[i] =


    其次,假设它在python中是copy-on-write,当obj在子进程中被修改时,int/File类型的对象确实改变了它的id,但对于list类型则不是这样

    不。区别与类型或写时复制无关。 obj = … 永远不会影响原始对象。不管是int 还是list

    你看到不同行为的原因不是类型不同,而是你有不同的代码。你不做obj = …,你做obj.append(…)。这是对象上的一个方法,它会就地改变它。

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


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


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

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

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

    ... 迭代 range(5, 4),它是空的,所以你不需要 join 任何东西。

    这是直接迭代集合更好的众多原因之一:

    for proc in p:
        proc.join()
    

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


    同时,即使它与您的实际代码完全无关,如果您对继承和不继承以及如何继承感兴趣,请阅读start methods 和相关的programming recommendations。但简而言之:如果您使用 fork 方法(非 Windows 系统上的默认值),您的全局变量将直接共享;如果您使用spawn(Windows 上的默认设置),它们将从源代码重新构建。但是如果你真的需要共享变量(在大多数情况下你真的不想这样做),你几乎不应该依赖fork 行为,即使你只打算在 Unix 上运行;使用明确的shared memory。 (另外,如果你真的需要共享任何可以被多个进程改变的东西,你真的需要一个Lock 或其他同步对象。)

    【讨论】:

    • 很抱歉在上面犯了一些错误,但这里还有一个问题。为什么子进程中的 id(obj) 与父进程中的原始打印值相同?当我们将它传递给子进程回调函数时,对 data[i] 的引用(即 z 或 file* 或 arr)是否应该改变?
    • @kuixiong 如果两个不同的变量引用同一个值,那么它们具有相同的id,因为id是该值的一个属性。
    • 虽然id是值的一个属性,但是当值的引用计数发生变化时,子进程应该复制这个值吧?例如,当我们调用mp.Process(target=func, args=(data[1], 1)).start()时,file的值的引用会改变,那么子进程应该复制文件对象,结果obj不会和file引用同一个对象做。这只是我的拙见,希望您能纠正我的误解,谢谢!
    猜你喜欢
    • 2015-03-15
    • 1970-01-01
    • 2015-11-18
    • 2013-08-19
    • 2012-12-16
    • 2014-01-11
    • 2021-12-27
    • 1970-01-01
    • 2021-09-13
    相关资源
    最近更新 更多