2017-06-22 121 views
2

最終目標是在後臺執行一個方法,但不是並行執行:當多個對象調用這個方法時,每個方法都應該等待輪到他們進行。要實現在後臺運行,我必須在子進程(而不是線程)中運行該方法,並且需要使用spawn(而不是fork)來啓動它。爲了防止並行執行,顯而易見的解決方案是在進程之間共享全局鎖。
當分叉進程(這是Unix上的默認進程)時,很容易實現,如以下兩個代碼中突出顯示的那樣。
我們可以分享它作爲一個類變量:Python:在產生的進程之間共享一個鎖

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 

    l = mp.Lock() 

    def f(self): 
     with self.l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    p1 = mp.Process(target = a.f) 
    p2 = mp.Process(target = b.f) 
    p1.start() 
    p2.start() 

或者我們可以把它傳遞給方法:

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = mp.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

這兩個代碼具有打印「你好」的適當的行爲以一秒的間隔。 但是,將start method更改爲「產卵」時,它們會斷開。
第一個(1)同時打印「hello」。這是因爲the internal state of a class is not pickled,所以他們沒有相同的鎖。
第二個(2)在運行時出現FileNotFoundError失敗。我認爲這與鎖不能被醃製的事實有關:請參閱Python sharing a lock between processes
在這個答案中,建議了兩個修正(附註:我不能使用一個池,因爲我想隨機創建任意數量的進程)。
我還沒有找到一種方法,以適應第二修復,但我試圖執行的第一個:

import multiprocessing as mp 
from time import sleep 

if __name__ == "__main__": 
    mp.set_start_method('spawn') 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

這種失敗,AttributeError的和FileNotFoundError(3)。事實上,當使用fork方法時,它也會失敗(BrokenPipe)(4)。
共享生成的進程之間鎖定的正確方法是什麼?
這四個失敗的快速解釋我編號也會很好。 我在Archlinux下運行Python 3.6。

+0

聽起來你已經仔細閱讀了文檔。您是否查看了[17.2.1.5](https://docs.python.org/3.6/library/multiprocessing.html#sharing-state-between-processes)中的解決方案?你應該能夠將鎖放入共享內存或管理器中,對嗎? –

+0

我試過了,它在第三個代碼片段中。它沒有工作,但可能有很多方法可以做到這一點,其中包括一個可以實現這一點的方法。 – Zil0

+0

對不起,我應該在評論之前進一步研究代碼。 :-) –

回答

1

最後的代碼片段可以工作,只要腳本不會過早退出。加盟流程是不夠的:在錯誤

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    mp.set_start_method('spawn') 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 
    p1.join() 
    p2.join() 

更多信息這是造成這裏https://stackoverflow.com/a/25456494/8194503

+0

現在你已經提到過了,我應該馬上想到這一點。良好的研究,無論是在問題和答案。 –

1

恭喜你,你已經有了90%的路程。最後一步實際上並不是很難。

是的,您的最終代碼塊會失敗,出現AttributeError錯誤,但具體是什麼錯誤? 「無法獲取屬性'OneAtATime'on」。這與您遇到的問題非常相似 - 它不會酸洗OneAtATime類。

我做了如下的變化和它的工作,只要你願意:

file ooat.py:

from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

interactive shell:

import multiprocessing as mp 
from oaat import OneAtATime 
if __name__ == "__main__": 
    mp.set_start_method('spawn') 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

您可能注意到,我沒有做任何事情 - 只是分割你的代碼分成兩個單獨的文件。試試看,你會發現它工作正常。 (至少,它對我來說,在Ubuntu上使用python 3.5)

+0

當我看到我忘記用spawn方法測試它時,我暫時刪除了這個答案。然後當我測試時,它說「OneAtATime沒有定義」,但只是因爲我忘記了從ooat導入OneAtATime'行。所以現在我明顯測試了fork和spawn,並且它工作正常。 –

+0

答案在您的文章中,但不是您的文章。它是「交互式外殼」。事實上,我得到的錯誤是:AttributeError:'ForkAwareLocal'對象沒有屬性'連接'。 在那裏,我停止了嘗試並寫下了這個問題,但我從實際的解決方案中搜索了一下:https://stackoverflow.com/a/25456494/8194503。 它在我的同一個文件中工作得很好,不知道你的錯誤是什麼。我將用修復程序編輯我的問題。 不知道禮儀是什麼,如果我接受你的答案,我猜它接受它。謝謝你的時間 ! – Zil0

+1

如果你已經解決了你的問題,但沒有,你應該發佈你的答案,等待它告訴你的任何時間,並接受你自己的答案。這樣做非常合適,隨着時間的推移,您的答案會得到提升。 –