2014-09-03 49 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