#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 的作用只是像個閘門,可以讓多少線程通過這道
門,而不是讓多少線程可以鎖住使用權
沒有留言:
張貼留言