Python-多進程共享數據1

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
最終的value值很可能不是500。
原因如下:multiprocessing.Value的默認鎖是非常細粒度的,Value.value表示了內存中的ctypes對象,Value是對ctypes對象的包裝,Value能保證只有一個進程或線程能同時讀寫value屬性,這是因爲讀操作和寫操作有時並不是原子操作,也就是說一個寫操作可能需要幾個CPU指令,而此時另一個進程或線程可能在寫操作完成之前讀取value值,就會讀取錯誤值,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

2.https://docs.python.org/2/library/multiprocessing.html

3.https://www.jb51.net/article/165430.htm

 
發佈了18 篇原創文章 · 獲贊 7 · 訪問量 7231
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章