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。