python的multiprocessing模塊提供了兩個方法來共享數據:Value和Array
- Array: 從共享內存中分配ctypes數組
- Value: 從共享內存中分配ctypes對象
對於單個數字,字符或者字符串類型的同步,使用multiprocessing中的Value方法即可。
Value(typecode_or_type, *args[, lock])
該方法從共享內存中分配一個ctypes 對象,返回值實際上是對象的同步包裝器(synchronized wrapper)。可以通過value屬性訪問對象本身。其中typecode_or_type定義了分配對象的類型。typecode_or_type可以是一個ctypes類型,或是一個代表ctypes類型的字符代碼。具體對應關係見下表
例如,以下兩段代碼等價
from ctypes import c_int
i=Value(c_int,10)#等價
i=Value('i',10)
示例代碼:
from multiprocessing import Process,Value
def f(n):
n.value=n.value-1
if __name__=='__main__':
n=Value('i',10)
print(n.value)
p=Process(target=f,args=(n,))
p.start()
p.join()
print(n.value)
輸出結果
10
9
注意,p.join()如果不寫會導致輸出結果都爲10,因爲此時子進程還沒有修改變量主進程就輸出了n值,加入p.join()後主進程將等待子進程執行完畢後再繼續執行
對於多個進程或線程修改共享變量,需要注意“鎖”的問題。
舉例:
import time
from multiprocessing import Process, Value
def func(val):
for i in range(50):
time.sleep(0.01)
val.value += 1
if __name__ == '__main__':
v = Value('i', 0)
procs = [Process(target=func, args=(v,)) for i in range(10)]
for p in procs: p.start()
for p in procs: p.join()
print v.value
val.value +=1
包含了讀操作和寫操作,實際執行的是如下指令:
0 LOAD_FAST 0 (val)
3 DUP_TOP
#<--- Value lock acquired
4 LOAD_ATTR 0 (value)
#<--- Value lock released
7 LOAD_CONST 1 (1)
10 INPLACE_ADD
11 ROT_TWO
#<--- Value lock acquired
12 STORE_ATTR 0 (value)
#<--- Value lock released
也就是說,在讀和寫操作之間,完全有可能有另一個線程獲取鎖並且讀取錯誤值,導致最終值不是500。
解決方法比較簡單,就是使用一個單獨的鎖,在該語句執行前加上獲取鎖的操作:
import time
from multiprocessing import Process, Value, Lock
def func(val, lock):
for i in range(50):
time.sleep(0.01)
with lock:
val.value += 1
if __name__ == '__main__':
v = Value('i', 0)
lock = Lock()
procs = [Process(target=func, args=(v, lock)) for i in range(10)]
for p in procs: p.start()
for p in procs: p.join()
print v.value
參考:
1.https://eli.thegreenplace.net/2012/01/04/shared-counter-with-pythons-multiprocessing