首页 > 解决方案 > 在 Python 中进行多处理时,在类实例中通过 self 传递参数

问题描述

它似乎有效,但是self分叉​​后使用是否安全?或者我应该始终将参数作为函数参数传递给子进程args吗?

import multiprocessing as mp

class C():

    def __init__(self):
        self.v = 'bla'
        p = mp.Process(target=self.worker, args=[])
        #p = mp.Process(target=self.worker, args=(self.v,))
        p.start()
        p.join()

    def worker(self):
        print(self.v)

    #def worker(self, v):
        #print(v)

c = C()

# prints 'bla'

更具体地说,我想传递 manager.Queue() 对象,不确定它是否有所作为。

如果这是一个简单的 C fork(),因为整个过程被相同地复制 - 除了 pid -,self将是相同的。但是 Python 多处理可能正在做一些我不知道的事情,或者在某处可能会出现警告,例如“不要这样使用它,这可能会在未来发生变化”。我没有找到任何专门解决这个问题的东西。

我真正担心的是传入的参数args,特别是如果它们与多处理模块相关联,可能会围绕 fork() 进行转换以避免任何问题。

Python 3.6.5

标签: pythonpython-3.xclassmultiprocessingfork

解决方案


对于除fork start 方法之外的任何方法,在调用时,目标和参数都使用pickling发送到工作进程Process.start()。对于fork方法,子进程在同一点被fork,所以Process.start()调用when。

所以当你不使用fork start方式的时候,你需要担心的是你的数据是否会被pickle。在这种情况下,没有理由避免使用类实例和self;整个实例被腌制为self.target包含对该实例的引用的方法:

>>> class C:
...     def __init__(self):
...         self.v = 'bla'
...     def worker(self):
...         print(self.v)
...
>>> c = C()
>>> data = pickle.dumps(c.worker)
>>> pickletools.dis(data)
    0: \x80 PROTO      4
    2: \x95 FRAME      71
   11: \x8c SHORT_BINUNICODE 'builtins'
   21: \x94 MEMOIZE    (as 0)
   22: \x8c SHORT_BINUNICODE 'getattr'
   31: \x94 MEMOIZE    (as 1)
   32: \x93 STACK_GLOBAL
   33: \x94 MEMOIZE    (as 2)
   34: \x8c SHORT_BINUNICODE '__main__'
   44: \x94 MEMOIZE    (as 3)
   45: \x8c SHORT_BINUNICODE 'C'
   48: \x94 MEMOIZE    (as 4)
   49: \x93 STACK_GLOBAL
   50: \x94 MEMOIZE    (as 5)
   51: )    EMPTY_TUPLE
   52: \x81 NEWOBJ
   53: \x94 MEMOIZE    (as 6)
   54: }    EMPTY_DICT
   55: \x94 MEMOIZE    (as 7)
   56: \x8c SHORT_BINUNICODE 'v'
   59: \x94 MEMOIZE    (as 8)
   60: \x8c SHORT_BINUNICODE 'bla'
   65: \x94 MEMOIZE    (as 9)
   66: s    SETITEM
   67: b    BUILD
   68: \x8c SHORT_BINUNICODE 'worker'
   76: \x94 MEMOIZE    (as 10)
   77: \x86 TUPLE2
   78: \x94 MEMOIZE    (as 11)
   79: R    REDUCE
   80: \x94 MEMOIZE    (as 12)
   81: .    STOP
highest protocol among opcodes = 4

在上面的流中你可以清楚地看到v'blah'worker命名。

如果你确实使用了fork start 方法,那么子进程就可以完全访问父进程内存中的所有内容;self仍在引用您在分叉之前拥有的相同对象。您的操作系统会处理那里的细节,例如确保文件描述符是独立的,以及确保子进程获取正在更改的内存块的副本。

无论哪种方式,您对实例所做的进一步更改对父进程都不可见,除非您明确使用设计为共享的数据结构


推荐阅读