我有一个名为Experiment的类,另一个名为Case。一个实验由许多个案组成。请参阅下面的类定义,
from multiprocessing import Process
class Experiment (object):
def __init__(self, name):
self.name = name
self.cases = []
self.cases.append(Case('a'))
self.cases.append(Case('b'))
self.cases.append(Case('c'))
def sr_execute(self):
for c in self.cases:
c.setVars(6)
class Case(object):
def __init__(self, name):
self.name = name
def setVars(self, var):
self.var = var
在我的实验课中,我有一个名为sr_execute的函数。此功能显示所需的行为。我有兴趣解析所有案例并为每个案例设置一个属性。当我运行以下代码时,
if __name__ == '__main__':
#multiprocessing.freeze_support()
e = Experiment('exp')
e.sr_execute()
for c in e.cases: print c.name, c.var
我明白了,
a 6
b 6
c 6
这是理想的行为。
但是,我想使用多处理并行执行此操作。为此,我向实验类添加了一个mp_execute()函数,
def mp_execute(self):
processes = []
for c in self.cases:
processes.append(Process(target= c.setVars, args = (6,)))
[p.start() for p in processes]
[p.join() for p in processes]
然而,这不起作用。当我执行以下操作时,
if __name__ == '__main__':
#multiprocessing.freeze_support()
e = Experiment('exp')
e.mp_execute()
for c in e.cases: print c.name, c.var
我收到错误,
AttributeError: 'Case' object has no attribute 'var'
显然,我无法使用多处理设置class属性。
任何线索,
答案 0 :(得分:3)
致电时:
def mp_execute(self):
processes = []
for c in self.cases:
processes.append(Process(target= c.setVars, args = (6,)))
[p.start() for p in processes]
[p.join() for p in processes]
当您创建Process
时,它将使用您对象的副本,并且对此类对象的修改不传递给主程序,因为不同的进程有不同的地址空间。如果您使用Thread
,它会起作用
因为在这种情况下没有创建副本。
另请注意,您的代码可能会在Windows中失败,因为您将方法作为target
传递,Windows要求target
可以选择(实例方法不可拾取)。
target
应该是在模块顶层定义的函数,以便处理所有Oses。
如果您想与主流程进行通信,您可以进行以下更改:
无论如何,必须通过设置“频道”(如Queue
)或设置共享状态来“明确”处理通信。
样式注释:不以这种方式使用list-comprehensions:
[p.join() for p in processes]
它只是错误。您只是在浪费空间来创建None
的列表。与右方式相比,它也可能更慢:
for p in processes:
p.join()
因为必须将元素附加到列表中。
有人说列表推导比for
循环略快,但是:
当且仅当考虑这种循环时,它们才会更快
a = []
for element in something:
a.append(element)
如果循环(如本案例中)不创建list
,那么for
循环会更快。
顺便说一下:有些人以同样的方式使用map
来执行副作用。这又是错误的,因为你没有获得太多的速度,原因与之前和在python3中完全失败,其中map
返回一个迭代器,因此它会不完全执行这些功能,从而降低了代码的可移植性。
答案 1 :(得分:1)
@ Bakuriu的回答提供了良好的造型和效率建议。确实,每个进程都获得了主进程堆栈的副本,因此分叉进程所做的更改不会反映在主进程的地址空间中,除非您使用某种形式的IPC(例如Queue,管道,经理)。
但是您遇到的特定AttributeError: 'Case' object has no attribute 'var'
错误还有其他原因,即您的Case对象 在您启动时仍具有var
属性流程。相反,var
属性是在setVars()
方法中创建的。
你的分叉进程在调用setVars()
时确实创建了变量(实际上甚至将它设置为6),但是,这种变化只发生在Case对象的副本中,即没有反映在主进程的内存空间中(变量仍然不存在)。
要了解我的意思,请将您的Case类更改为:
class Case(object):
def __init__(self, name):
self.name = name
self.var = 7 # Create var in the constructor.
def setVars(self, var):
self.var = var
通过在构造函数中添加var
成员变量,您的主进程将可以访问它。当然,分叉进程中的更改仍然不会反映在主进程中,但至少不会出现错误:
a 7
b 7
c 7
希望这能够揭示正在发生的事情。 =)
<强> SOLUTION:强>
最少侵入性(对原始代码)要做的是使用共享内存中的ctypes对象:
from multiprocessing import Value
class Case(object):
def __init__(self, name):
self.name = name
self.var = Value('i', 7) # Use ctypes "int" from shared memory.
def setVars(self, var):
self.var.value = var # Set the variable's "value" attribute.
并更改main()以打印c.var.value:
for c in e.cases: print c.name, c.var.value # Print the "value" attribute.
现在您有了所需的输出:
a 6
b 6
c 6