c++提供了互斥量:mutex
和條件變量:condition_variable
,但是并沒有信號量:semaphore
。而linux和windows系統庫會提供的。下面簡單介紹一下信號量的特性,然后給出一個簡單的demo,使用mutex + condition_variable 來實現信號量。
信號量的定義:
信號量是一個整數count,提供兩個原子操作:P操作和V操作
P操作(wait操作):count減1;如果count < 0,那么掛起執行線程
V操作(signal操作):count加1;如果count <= 0,那么喚醒一個執行線程
mutex互斥量相當于一把鎖,lock的狀態為0 ,1,也就是lock狀態與unlock狀態。如果現在有多把鎖,數量count最初被設定為n,取1把鎖count–,如果發現鎖的數量小于0,也就是沒有鎖了,此時就需要wait,也可以說是suspend or block,直到別人釋放出一把鎖。取完鎖要還一把鎖,也就是count++,如果發現此時count<=0,說明此時有人在等待鎖,就喚醒一個等待的線程,把鎖給他。
當count = 1時,就相當于mutex了。
如何實現信號量:
1、首先需要一個int or long變量作為count;
2、然后由于PV操作是原子操作,所以至少需要一個mutex來保證互斥
3、需要掛起線程,又需要喚醒線程,所以也需要用到condition_variable
代碼:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
namespace Solution1 {class semaphore {private:int count;int wakeups; // 輔助變量,要喚醒的線程數,初值為0std::mutex mutex;std::condition_variable cond;public:semaphore(int value = 1) : count(value), wakeups(0) {}void wait() // P操作{std::unique_lock<std::mutex> lock(mutex);if (--count < 0) {cond.wait(lock, [&]()->bool{return wakeups > 0;});--wakeups;}}void signal() // V操作{std::lock_guard<std::mutex> lock(mutex);if (++count <= 0) {++wakeups;cond.notify_one();}}};std::mutex printMutex;Solution1::semaphore ba(0) , cb(0), dc(0);void a(){ba.wait(); // b->astd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread a" <<std::endl;}void b(){cb.wait(); // c->bstd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread b" <<std::endl;ba.signal(); // b->a}void c(){dc.wait(); // d->cstd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread c" <<std::endl;cb.signal(); // c->b}void d(){std::lock_guard<std::mutex> lock(printMutex);std::cout << "thread d" <<std::endl;dc.signal(); // d->c}
};namespace Solution2 {class semaphore {private:int count;std::mutex mutex;std::condition_variable cond;public:semaphore(int value = 1) : count(value){}void wait() // P操作{std::unique_lock<std::mutex> lock(mutex);if (--count < 0) {cond.wait(lock);}}void signal() // V操作{std::lock_guard<std::mutex> lock(mutex);if (++count <= 0) {cond.notify_one();}}};std::mutex printMutex;Solution1::semaphore ba(0) , cb(0), dc(0);void a(){ba.wait(); // b->astd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread a" <<std::endl;}void b(){cb.wait(); // c->bstd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread b" <<std::endl;ba.signal(); // b->a}void c(){dc.wait(); // d->cstd::lock_guard<std::mutex> lock(printMutex);std::cout << "thread c" <<std::endl;cb.signal(); // c->b}void d(){std::lock_guard<std::mutex> lock(printMutex);std::cout << "thread d" <<std::endl;dc.signal(); // d->c}
};// 測量一個函數的運行時間
template <class T>
void measure(T&& func) {using namespace std::chrono;auto start = system_clock::now();// funcfunc();duration<double> diff = system_clock::now() - start;std::cout << "執行了" << diff.count() << "秒" << std::endl;
}int main()
{measure([](){std::thread th1(Solution1::a), th2(Solution1::b), th3(Solution1::c), th4(Solution1::d);th1.join();th2.join();th3.join();th4.join();std::cout << "ending" << std::endl;});measure([](){std::thread th1(Solution2::a), th2(Solution2::b), th3(Solution2::c), th4(Solution2::d);th1.join();th2.join();th3.join();th4.join();std::cout << "ending" << std::endl;});return 0;
}
在linux系統下運行效果如下:
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ touch main.cpp
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ vim main.cpp
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ g++ -pthread -o main main.cpp
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000541065秒
thread d
thread c
thread b
thread a
ending
執行了0.000292463秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000422226秒
thread d
thread c
thread b
thread a
ending
執行了0.000189556秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000549342秒
thread d
thread c
thread b
thread a
ending
執行了0.000312412秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000521796秒
thread d
thread c
thread b
thread a
ending
執行了0.00043399秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000426875秒
thread d
thread c
thread b
thread a
ending
執行了0.000244544秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000445409秒
thread d
thread c
thread b
thread a
ending
執行了0.000345057秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000377516秒
thread d
thread c
thread b
thread a
ending
執行了0.000258996秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000523052秒
thread d
thread c
thread b
thread a
ending
執行了0.00027911秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000517352秒
thread d
thread c
thread b
thread a
ending
執行了0.000395749秒
dyy@dyy-Lenovo-ThinkBook-14-IIL:~/Desktop/HeartBeat$ ./main
thread d
thread c
thread b
thread a
ending
執行了0.000488651秒
thread d
thread c
thread b
thread a
ending
執行了0.000392515秒
參考
c++11中信號量(semaphore)的實現 | 陸仁賈
深層次探討mutex與semaphore之間的區別
下面的這個代碼比較復雜:
線程同步之信號量,代碼實現方法2(條件變量+mutex互斥量)