2014-09-24 52 views
0

我想我已經在這裏找到了類似的問題,但沒有找到一個很容易理解的好解釋。我也嘗試閱讀python文檔,但也沒有在那裏找到詳細的解釋。在進程間修改共享字典對象不起作用

現在,這裏是代碼:

from multiprocessing import Process, Manager 

def f(d): 
    d[1] = [[1]] 
    if 2 in d.keys(): 
     d[2].append([2]) 
    else: 
     d[2] = [[2]] 
    d[3] = [[3]] 

if __name__ == '__main__': 

    with Manager() as manager: 
     d = manager.dict() 

     p1 = Process(target=f, args=(d,)) 
     p2 = Process(target=f, args=(d,)) 
     p1.start() 
     p2.start 
     p1.join() 
     p2.join 

     print(d) 

我有兩個進程運行和共享來自管理器進程的字典對象。我可以將新的鍵值對添加到字典對象,但如果鍵已經存在,則不能修改值。

這是爲什麼?如果我們使用這樣的共享對象,如果想要修改,會出現什麼問題?

回答

1

首先,你沒有開始第二個過程。

p2.start # <--- no `()` 
p2.join # <--- no `()` 

其次,根據multiprocessing.managers.SyncManager documentation NOTE

注意情況:對可變值或項字典和列表代理將 不通過管理器來傳播,因爲代理沒有辦法 知道其值或項目何時被修改。 若要修改這樣的項目, 您可以將修改後的對象重新分配給容器代理。

所以,你的程序應該是這樣的:

from multiprocessing import Process, Manager 

def f(d): 
    d[1] = [[1]] 
    if 2 in d: # NOTE: You don't need to use `keys()`. `dict` support `in` operator. 
     d[2] = d[2] + [[2]] 
    else: 
     d[2] = [[2]] 
    d[3] = [[3]] 

if __name__ == '__main__': 

    with Manager() as manager: 
     d = manager.dict() 

     p1 = Process(target=f, args=(d,)) 
     p2 = Process(target=f, args=(d,)) 
     p1.start() 
     p2.start() 
     p1.join() 
     p2.join() 

     print(d) 

但是,還有另一種擔憂。以下語句涉及多次讀取,寫入共享對象;這不是原子的;所以不能保證上述程序每次都會產生相同的結果。

if 2 in d: 
    d[2] = d[2] + [[2]] 
else: 
    d[2] = [[2]] 

您需要使用鎖來確保只有一個進程訪問共享對象。

+0

我做了一個演示原子性問題的例子。 http://ideone.com/N7d99s(沒有包括,因爲我不想延長答案太長) – falsetru 2014-09-24 14:41:53

+0

帶鎖的修改示例:http://ideone.com/TyVKs9 – falsetru 2014-09-24 14:44:22

+0

感謝它的工作。但早些時候我也試過(不知道原因)mylist = d [2]; mylist = mylist.append([2]); d [2] = mylist。但它沒有奏效。有什麼區別? – bluefoggy 2014-09-24 14:46:18