#include <mutex> #include <iostream> #include <condition_variable>
// 適用於 C++11 的 Semaphore // 因 C++11 沒提供 class Semaphore { unsigned int m_ThreadMaxNum; // 最多可多少 thread 允許通行 std::mutex m_Semaphore_mutex; std::condition_variable m_condition;
public: // Constructor // ThreadMaxNum = 最多可多少 thread 允許通行 Semaphore(unsigned int ThreadMaxNum) { m_ThreadMaxNum = ThreadMaxNum; } // Destructor ~Semaphore() { }
// 進入關鍵區域 void Enter() { std::unique_lock<std::mutex> lock{ m_Semaphore_mutex }; m_condition.wait(lock, [&]()->bool{return m_ThreadMaxNum > 0;} ); --m_ThreadMaxNum; }
// 離開關鍵區域 void Leave() { std::lock_guard<std::mutex> lock{ m_Semaphore_mutex }; ++m_ThreadMaxNum; m_condition.notify_one(); }
// 輔助類別 // 幫助自動叫用 Semaphore::Enter() 和 Semaphore::Leave() // 對於回傳值的函數必須使用 class SemaphoreHandle { Semaphore &S; public: SemaphoreHandle(const Semaphore &Semaphore_Arg) :S(const_cast<Semaphore &> (Semaphore_Arg)) { S.Enter(); }
~SemaphoreHandle() { S.Leave(); } }; };
// 使用範例 class A { Semaphore m_Semaphore; public: A() :m_Semaphore(5) // 最多只能有 5 個 thread 同時使用 {} ~A(){}
void Fn1() { m_Semaphore.Enter(); std::cout << "Fn1" << std::endl; m_Semaphore.Leave(); }
void Fn2() { Semaphore::SemaphoreHandle AutoSH(m_Semaphore); std::cout << "Fn2" << std::endl; }
int Fn3() { // 對於有回傳值的函數必須使用,以確保叫用 return 後 // 才呼叫 Semaphore::Leave() Semaphore::SemaphoreHandle AutoSH(m_Semaphore); std::cout << "Fn3" << std::endl; return 0; } };
了解一下 Semaphore::Enter() 到底做了什麼事
void Enter()
{
// 搶奪 m_Semaphore_mutex 以鎖住取得繼續執行的權力
std::unique_lock<std::mutex> lock{ m_Semaphore_mutex };
// 會執行至此表示已鎖住 m_Semaphore_mutex
m_condition.wait(lock,
// 在 m_Semaphore_mutex 鎖住的情形下,
// 檢查 m_ThreadMaxNum > 0,
// 若為 true,繼續往下執行
// 若為 false,會將目前線程的 lock 保存起來,並把
// 目前線程 block,把 m_Semaphore_mutex 解鎖
// 當 m_condition 獲得 notify 時,會任選一個保
// 存的線程解除 block,讓該線程的 lock 加入
// m_Semaphore_mutex 的搶奪,再重覆以上縮排的處理
[&]()->bool{return m_ThreadMaxNum > 0;} );
// 在 m_Semaphore_mutex 鎖住的情形下,改變 m_ThreadMaxNum
// 的值
--m_ThreadMaxNum;
} // 離開區塊,會自動引發 lock 的解構函數,將 m_Semaphore_mutex 解鎖
注意,發出 notify 的線程須先取得鎖住 m_Semaphore_mutex 的權
力,Semaphore::Leave() 已做到
再用例子來了解它的意義,直接使用 Semaphore 的樣貌如下
Semaphore Semaphore(10); void F1() { Semaphore.Enter(); while (true); Semaphore.Leave(); }這樣最多只能有 10 個線程在跑那跑不出來的 loop。
若把 Semaphore 展開來用,樣貌如下
unsigned int ThreadMaxNum = 10; // 最多可多少 thread 允許通行 std::mutex Semaphore_mutex; std::condition_variable condition; void F2() { std::unique_lock<std::mutex> lock1{ Semaphore_mutex }; condition.wait(lock1, [&]()->bool{return ThreadMaxNum > 0; } ); --ThreadMaxNum; while (true);
std::lock_guard<std::mutex> lock2{ Semaphore_mutex }; ++ThreadMaxNum; condition.notify_one(); }但這樣最多只能有 1 個線程在跑那跑不出來的 loop。
必須改寫成這樣才正確
void F2() { { std::unique_lock<std::mutex> lock1{ Semaphore_mutex }; condition.wait(lock1, [&]()->bool{return ThreadMaxNum > 0; } ); --ThreadMaxNum; } while (true);
{ std::lock_guard<std::mutex> lock2{ Semaphore_mutex }; ++ThreadMaxNum; condition.notify_one(); } }藉由區塊的結束執行 lock1 及 lock2 的解構函數。
所以 Semaphore 的作用只是像個閘門,可以讓多少線程通過這道
門,而不是讓多少線程可以鎖住使用權
沒有留言:
張貼留言