1. 程式人生 > >Python並行程式設計(五):執行緒同步之訊號量

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。