python - 在 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
解决方案
对于除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
仍在引用您在分叉之前拥有的相同对象。您的操作系统会处理那里的细节,例如确保文件描述符是独立的,以及确保子进程获取正在更改的内存块的副本。
无论哪种方式,您对实例所做的进一步更改对父进程都不可见,除非您明确使用设计为共享的数据结构。