1. 程式人生 > 程式設計 >python multiprocessing多程序變數共享與加鎖的實現

python multiprocessing多程序變數共享與加鎖的實現

python多程序和多執行緒是大家會重點了解的部分,因為很多工作如果並沒有前後相互依賴關係的話其實順序並不是非常的重要,採用順序執行的話就必定會造成無謂的等待,任憑cpu和記憶體白白浪費,這是我們不想看到的。

為了解決這個問題,我們就可以採用多執行緒或者多程序的方式,(多執行緒我們之後再講),而這兩者之間是有本質區別的。就記憶體而言,已知程序是在執行過程中有獨立的記憶體單元的,而多個執行緒是共享記憶體的,這是多程序和多執行緒的一大區別。

利用Value在不同程序中同步變數

在多程序中,由於程序之間記憶體相互是隔離的,所以無法在多個程序中用直接讀取的方式共享變數,這時候就可以用multiprocessing庫中的 Value在各自隔離的程序中共享變數。

下面是一個多程序的例子:

假設有一個counter用來記錄程式經過的總迴圈次數,每呼叫一次count函式之後counter就會增加20,在主程式中用迴圈開10個程序分別呼叫count函式,那麼理想狀態下,在十個程序中共享的counter值到程式結束後應該是200。

from multiprocessing import Process,Value
import time

def count(v):
  for i in range(20):
    time.sleep(0.01)
    v.value += 1

def main():
  value = Value('i',0)
  processes = [Process(target=count,args=(value,)) for i in range(10)]

  for p in processes:
    p.start()
  for p in processes:
    p.join()

  print(value.value)

if __name__ == '__main__':

  for i in range(10):
    main()

執行這個例子,會得到怎樣的結果呢?

188
180
168
186
183
179
186
181
166
186

我在主程式裡運行了十次這個程式,而最後的結果是160-180之間,總之,沒有一次到200。這是什麼原因呢?

相信很多人都已經明白了問題所在,那就是因為在multiprocessing庫中的Value是細粒度的,Value中有一個ctypes型別的物件,擁有一個value屬性來表徵記憶體中實際的物件。Value可以保證同時只有一個單獨的執行緒或程序在讀或者寫value值。這麼看起來沒有什麼問題。

然而在第一個程序載入value值的時候,程式卻不能阻止第二個程序載入舊的值。兩個程序都會把value拷貝到自己的私有記憶體然後進行處理,並寫回到共享值裡。

那麼這麼會出現什麼問題呢?

最後的共享值只接收到了一次值的增加,而非兩次。

利用Lock在不同程序共享變數時加鎖

上面的問題其實可以用一個非常簡單的方法解決,我們只需要呼叫multiprocessing庫中的Lock (鎖)就可以保證一次只能有一個程序訪問這個共享變數。修改後的程式碼如下:

from multiprocessing import Process,Value,Lock
from time import sleep

def count(x,lock):
  for i in range(20):
    sleep(0.01)
    with lock:
      x.value += 1


def main():
  counter = Value('i',0)
  lock = Lock()
  processes = [Process(target=count,args=(counter,lock)) for i in range(10)]
  for p in processes:
    p.start()
  for p in processes:
    p.join()

  print(counter.value)

if __name__ == '__main__':
  for i in range(10):
    main()

這樣一來,輸出的結果就會恆定為200了。

一些補充

1. 呼叫get_lock() 函式

其實Value這個包裡已經包含了鎖的概念,如果呼叫get_lock() 函式就可以自動給共享變數加鎖。這樣其實是比較推薦的方式,因為這樣就不需要同時呼叫兩個包。修改如下:

from multiprocessing import Process,Value
from time import sleep

def count(x):
  for i in range(20):
    global counter # 宣告全域性變數
    sleep(0.01)
    with counter.get_lock(): # 直接呼叫get_lock()函式獲取鎖
      x.value += 1

def main():
  processes = [Process(target=count,)) for i in range(10)]
  for p in processes:
    p.start()
  for p in processes:
    p.join()

  print(counter.value)

if __name__ == '__main__':
  counter = Value('i',0) # 需要把全域性變數移到主程式
  main()

上面的程式更加明確,且最終結果也是200。

2. 使用 multiprocessing.RawValue

整個multiprocessing包裡剛剛呼叫的Value和Lock還可以統一被 multiprocessing.RawValue取代。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。