Python並行程式設計(五):執行緒同步之訊號量
1、基本概念
訊號量是由作業系統管理的一種抽象資料型別,用於在多執行緒中同步對共享資源的使用。本質上說,訊號量是一個內部資料,用於標明當前的共享資源可以有多少併發讀取。
同樣在threading中,訊號量有acquire和release兩個函式。
- 每當執行緒想要讀取關聯了訊號量的共享資源時,必須呼叫acquire,此操作減少訊號量的內部變數,如果此變數的值非負,那麼分配該資源的許可權。如果是負值,那麼執行緒被掛起,直到有其他的執行緒釋放資源。
- 當執行緒不再需要該共享資源,必須通過release釋放,這樣,訊號線的內部變數增加,在訊號量等待佇列中排在最前面的執行緒會拿到共享資源的許可權。
訊號量同步機制線上程操作為原子操作時,才會沒有問題,但如果不是原子操作,或者兩個操作有一個終止了,就會出現問題,比如:
有兩個併發執行緒,都在等待一個訊號量,假設目前訊號量的內部值為1,再假設執行緒A將訊號量的值從1減到0,此時執行緒A拿到資源許可權,這時候如果控制器切換到了執行緒B,執行緒B將訊號量的值從0減到-1,並且在這裡被掛起等待,這時控制器回到執行緒A,訊號量已經成為了負值,於是第一個執行緒也在等待。儘管當時的訊號量是可以讓執行緒訪問資源的,但是因為非原子操作導致了所有的執行緒都在狀態。
2、訊號量的使用
使用訊號量進行執行緒同步例子:
# coding: utf-8 import threading import time import random semaphore = threading.Semaphore(0) def consumer(): print("Consumer is waiting.") semaphore.acquire() print("Consumer notify: Consumed item number %s" %item) def producer(): global item time.sleep(10) item = random.randint(0, 100)print("Producer notify: Produced item number %s" %item) semaphore.release() if __name__ == "__main__": for i in range(0, 5): t1 = threading.Thread(target=producer) t2 = threading.Thread(target=consumer) t1.start() t2.start() t1.join() t2.join() print("Program terminated")
訊號量被初始化為0,semaphore = threading.Semaphore(0),目的是同步兩個或多個執行緒。執行緒必須並行執行,所以需要訊號量同步。
如果訊號量的計數器到了0,就會阻塞acquire方法,直到得到另一個執行緒的通知。如果訊號量的計數器大於0,就會對這個值-1然後分配資源。
3、補充
訊號量的一個特殊用法是互斥量。互斥量是初始值為1的訊號量,可以實現資料、資源的互斥訪問。
訊號量在支援多執行緒的程式語言中應用很廣,但是他也有可能造成死鎖的情況。例如,有一個執行緒t1,先等待訊號量s1,然後等待訊號量s2,而執行緒t2會先等待訊號量s2,然後再等待訊號量s1,這樣就會發生死鎖,導致t1等待s2,但是t2在等待s1。