2014-09-03 73 views
0

互斥锁在这个代码中执行它的工作吗?即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

这是,运行它:)也请让我知道这段代码中有什么奇怪的,也许这是我的问题的根源 – Kam 2014-09-03 20:26:46

+0

你期望它做什么工作?在每个报告的末尾添加一个换行符,然后再次运行。 – 2014-09-03 20:27:45

+0

@Kam你粘贴的代码绝对不会运行。 'if __main__ ==“__main __”'应该是'if __name__ =='__main __“',并且您在'app'和'apps'之间切换您的对象列表。 – dano 2014-09-03 20:29:44

回答

1

它只是做它运行的工作,如果你使用的是类POSIX平台。如果您使用的是Windows,则每个进程都会以完全不同的锁副本结束。

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的

您可以通过添加一些额外的跟踪和sleep声明见本平台使用os.fork()创建新进程,这意味着您在父进程中创建的会自动由子代继承。但是,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. 

为了传递一个Lockmap一个Pool功能,你需要使用一个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的局限性做的最好的。

+0

非常感谢你的解释,我实际上在Linux上运行,我不认为它会有任何区别 – Kam 2014-09-03 21:01:16

+0

@Kam你的代码应该在Linux上确实工作正常。尝试在锁定块内添加“睡眠”以查看自己。 – dano 2014-09-03 21:02:50

+0

@Kam:你可以在Linux上使用'set_start_method('spawn')'(Python 3.4+)来模拟Windows的行为。将全局锁定并通过Pool()的'initargs'参数传递给工作进程可能会更简单。 'def init(lock_):全局锁;锁= lock_' – jfs 2014-09-03 23:28:37