2009-12-18 101 views
7

我們正在考慮使用與後端分離的複雜GUI來重新構建一個大型應用程序,以使用新的(Python 2.6)多處理模塊。 GUI /後端接口使用兩個方向交換消息對象的隊列。天真地使用Python多處理會遇到什麼問題?

我剛剛斷定的一件事(暫時,但隨時可以確認)是「對象身份」不會保留在多處理接口中。目前,當我們的GUI向後端發佈消息時,它期望獲得相同的消息並將結果附加爲屬性。在某些情況下,它使用對象標識(if received_msg is message_i_sent:)來標識返回的消息......並且這看起來可能不適用於多處理。

這個問題是要問什麼「陷阱」這樣你已在實際使用中看到能想象一個會遇到在天真地使用多模塊,尤其是在重構現有的單過程中的應用。請說明您的答案是否基於實際經驗。獎勵積分爲問題提供可用的解決方法。

編輯:雖然我這個問題的意圖是一般收集的問題的描述,我覺得我犯了兩個錯誤:我把它從一開始社區維基(這可能讓很多人忽略了它,他們不會獲得聲望點),而且我包含了一個過於具體的例子,雖然我很欣賞答案,但可能會讓很多人錯過對一般答覆的請求。我可能會重新提出一個新問題並重新提出這個問題。就目前而言,我只接受一個答案,僅僅是關於這個問題,只要它與我包含的具體例子相關。感謝那些回答過的人!

+1

'message_i_sent.id = id(message_i_sent)'然後'如果received_msg.id == message_i_sent.id'可以解決身份識別(如果存在)。還有很多其他人。 – jfs 2009-12-18 01:37:47

+0

@JF,謝謝,我想我比我的選擇更好,它會使用一個鎖定保護的類屬性增加一個在Message.__ init__中的id。你的方法似乎不需要鎖定或遞增或類屬性,但仍然是完全健壯的。除非你或其他人先做,否則我會把它寫成答案。 – 2009-12-18 01:47:53

+0

@JF,嗯....仍然在思考,但我現在還不太確定。如果先前的ID爲1234的消息經過了接口,但是在發送端沒有引用,然後另一個消息得到了相同的ID()並且也被髮送,這次保留了引用,但是* first *消息然後回來,它會錯誤地匹配。跟着我? – 2009-12-18 01:50:25

回答

2

我沒有使用多重處理本身,但提出的問題與我在其他兩個領域的經驗類似:分佈式系統和對象數據庫。 Python對象身份可以是祝福和詛咒!

至於一般性問題,如果您正在重構的應用程序可以確認任務正在異步處理,則會有所幫助。如果不是這樣,你通常最終會管理鎖,並且使用單獨進程可能獲得的許多性能將會因等待這些鎖而丟失。我也會建議你花時間在各個流程上構建一些腳手架來進行調試。真正的異步過程往往比心智可以持有和驗證更多 - 或者至少是我的想法!

對於概述的具體情況,當物品排隊並返回時,我將在流程邊界管理對象標識。發送要處理的任務時,使用id()對任務進行註釋,並使用id()作爲密鑰將任務實例存儲在字典中。任務更新/完成時,從字典中檢索id()返回的確切任務,並將新更新的狀態應用於該任務。現在確切的任務,因此它的身份,將保持。

+0

@Shane,不錯(對於id()的想法)。由於我們的GUI後端消息相對較少(與In-GUI或後端消息卷相比),狀態複製不應構成巨大的負擔。如果標記和存儲的消息從不返回,則存在內存泄漏的可能性,但這應該是由錯誤引起的異常情況,而不是常規情況。 – 2009-12-18 13:25:32

+1

@Peter,我已經處理了這個問題,通過給消息條目添加一個超時值 - 無論是用戶提供的還是隱式的。如果在回覆到達之前超時已到期,請發送錯誤處理程序。您還將必須處理遲到的回覆。 對於狀態複製,您可以刪除從消息對象發送時不再需要的任何數據,並仍在發送方維護標識屬性。如果你的數據不是python對象還有其他的選擇。將mmap或memcached用於面向內存的數據,或使用對外部URL,文件和數據庫的引用。 – 2009-12-21 19:18:59

1

那麼,當然在非單身物體(例如「a是無」或「a是假」)上測試身份通常不是一個好的做法 - 它可能很快,但是一個非常快速的解決方法將交換「是」爲「==」測試和使用的增量計數器來確定身份:

# this is not threadsafe. 
class Message(object): 
    def _next_id(): 
     i = 0 
     while True: 
      i += 1 
      yield i 
    _idgen = _next_id() 
    del _next_id 

    def __init__(self): 
     self.id = self._idgen.next() 

    def __eq__(self, other): 
     return (self.__class__ == other.__class__) and (self.id == other.id) 

這可能是一個想法。

另外,請注意,如果您有大量「工作進程」,內存消耗可能遠遠大於基於線程的方法。

+0

謝謝艾倫。你所展示的就像我在給J.F.Sebastian的評論中提到的,除了我們需要圍繞增量操作進行線程鎖定之外。至於工作進程,在我們的例子中,想法是僅分離GUI以確保良好的用戶響應並且最小化GUI中用戶活動對後端操作的影響。 – 2009-12-18 13:29:30

+0

是的,我甚至對增加的東西不是線程安全做了評論,但我想我在切割和粘貼到網站時切碎了它。 對於您的使用案例,您可以考慮採用異步方法,您是否想過Twisted? – 2009-12-18 13:46:01

+0

後端實際上是多線程和異步的,有多個Reactors。它「僅稍微扭曲」......使用我們稱之爲「彎曲」的包裝。它不能有效地作爲純粹的異步(一個線程),所以我們堅持鎖定這樣的事情。雖然好想。 – 2009-12-18 15:44:35