互斥体是否在此代码中完成了它的工作?即sys.stdout.write受保护?
import sys
from multiprocessing import Pool, Lock
class ParentApp():
mutex=Lock()
def report(self,msg):
with ParentApp.mutex:
sys.stdout.write(msg)
class ChildApp1(ParentApp):
def print_report(self):
for i in xrange(100):
ParentApp.report(self, 'BLABLA')
class ChildApp2(ParentApp):
def print_report(self):
for i in xrange(100):
ParentApp.report(self, 'TESTTEST')
def runnable(app):
app.print_report()
def main():
app=[]
app.append(ChildApp1())
app.append(ChildApp2())
pool = Pool(len(apps))
pool.map(runnable, apps)
exit(0)
if __name__ == '__main__':
sys.exit(main())
PS:代码也在这里:http://pastebin.com/GyV3w45F
PS:我在Linux主机上运行
答案 0 :(得分:1)
如果你在类似Posix的平台上,它只会做它的工作。如果您使用的是Windows,则每个进程都会以完全不同的锁副本结束。
您可以通过添加一些额外的跟踪和sleep
语句来看到这一点:
class ParentApp():
mutex=Lock()
def report(self,msg):
print("\nGETTING for {}".format(msg))
with self.mutex:
print("GOT for {}".format(msg))
sys.stdout.write(msg)
sys.stdout.flush()
time.sleep(5)
在Linux上:
GETTING for BLABLA
GOT for BLABLA
BLABLA
GETTING for TESTTEST
< 5 second delay here>
在Windows上:
GETTING for BLABLA
GOT for BLABLA
BLABLA
GETTING for TESTTEST
GOT for TESTTEST
TESTTEST
<5 second delay here>
这是因为Posix平台使用os.fork()
创建新进程,这意味着您在父进程中创建的Lock()
会自动由子进程继承。但是,Windows没有os.fork
,因此它需要生成一个新进程,然后在子进程中重新导入您的模块。重新导入模块意味着ParentApp
被重新导入并重新执行,Lock
类属性也是如此。因此,您的父母和两个孩子最终都拥有自己独特的Lock
。
要解决此问题,您需要在父级中创建一个Lock
,然后将其传递给子级。对于您当前的体系结构而言,这实际上并不是一项微不足道的任务 - 您将Lock
对象作为参数传递给pool.map
,这将不允许您传递Lock
个对象。如果您尝试,您将获得例外:
RuntimeError: Lock objects should only be shared between processes through inheritance
您实际上开始 Lock
时,您只能将正常Process
个对象传递给儿童。一旦它们启动(就像你调用Pool
方法时那样),你会得到异常:
l = Lock()
p = Process(target=func, args=(l,)) # ok
p.start()
pool = Pool()
pool.apply(func, args=(l,)) # not ok, raises an exception.
为了将Lock
传递给像Pool
这样的map
函数,您需要使用multiprocessing.Manager
来创建共享锁。这是我建议做的事情:
import sys
from multiprocessing import Pool, Lock, get_context, Manager
import time
class ParentApp():
def __init__(self, mutex):
self.mutex = mutex
def report(self,msg):
with self.mutex:
sys.stdout.write(msg)
class ChildApp1(ParentApp):
def print_report(self):
for i in range(100):
ParentApp.report(self, 'BLABLA')
class ChildApp2(ParentApp):
def print_report(self):
for i in range(100):
ParentApp.report(self, 'TESTTEST')
def runnable(app):
app.print_report()
def main():
apps=[]
m = Manager()
lock = m.Lock()
apps.append(ChildApp1(lock))
apps.append(ChildApp2(lock))
pool = Pool(len(apps))
pool.map(runnable, apps)
if __name__ == '__main__':
sys.exit(main())
为了确保共享Lock
,我们需要让ParentApp
实际上将锁定对象作为参数。这不是一个很好的,因为它在课堂上完全独立,但我认为这是我们能够做到的最好的Windows限制。