通常來講,信號量(semaphore)\(S\),表示資源數量減去需求數量。信號量的值僅能由 PV 操做來改變。spa
執行一次 P 操做意味着請求一個單位資源,所以 \(S\) 的值減 1;當 \(S < 0\) 時,需求數大於資源數,即已經沒有可用資源,請求者必須等待。線程
執行一個 V 操做意味着釋放一個單位資源,所以 \(S\) 的值加 1;若 \(S \ge 0\),此時資源數目能知足需求,因此能夠喚醒一個在等待的請求者,讓它獲取資源。code
當 \(S < 0\),\(|S|\) 即沒有獲得資源的請求者數目,也就是阻塞的線程數。隊列
用互斥量和條件變量能夠模擬信號量的行爲:資源
use std::sync::Condvar; use std::sync::Mutex; pub struct Semaphore { sem: Mutex<i32>, con: Condvar, } impl Semaphore { pub fn new(sem: i32) -> Self { Self { sem: Mutex::new(sem), con: Condvar::new(), } } pub fn p(&self) { let mut sem = self.sem.lock().unwrap(); *sem -= 1; if *sem < 0 { let _ = self.con.wait(sem).unwrap(); } } pub fn v(&self) { let mut sem = self.sem.lock().unwrap(); *sem += 1; if *sem <= 0 { self.con.notify_one(); } } }
當信號量的初始值取 1,任何第二個請求資源者會陷入飢餓。從而同一時刻只能有一個線程訪問某個資源。同步
初始化一個信號量 mutex
爲 Semaphore::new(1)
,在使用臨界資源以前調用 mutex.p()
,使用完資源以後調用 mutex.v()
。即實現了資源的互斥訪問。it
設置起始資源爲大於 1 的正數,則 PV 操做維護資源同步。class
舉例說明,要實現有 3 個緩衝區的、MPMS 的隊列。抽象出兩種資源:緩衝區空間(room)和隊列中的數據(data)。隊列爲空的狀況下,有 3 個 room 資源,沒有 data 資源。dva
let room = Semaphore::new(3); let data = Semaphore::new(0); let queue = Queue::empty();
要往隊列裏添加元素,則須要一個 room 資源,操做完以後產生一個 data 資源。變量
room.p(); queue.push(x); data.v();
要從隊列頭取出元素,則須要一個 data 資源,操做完以後產生一個 room 資源。
data.p(); let x = queue.pop(); room.v();
(完)