使用mutex和条件变量实现信号量

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;    // 辅助变量,要唤醒的线程数,初值为0
        std::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->a
        std::lock_guard<std::mutex> lock(printMutex);
        std::cout << "thread a" <<std::endl;
    }
    void b()
    {
    
    
        cb.wait(); // c->b
        std::lock_guard<std::mutex> lock(printMutex);
        std::cout << "thread b" <<std::endl;
        ba.signal(); // b->a
    }
    void c()
    {
    
    
        dc.wait(); // d->c
        std::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->a
        std::lock_guard<std::mutex> lock(printMutex);
        std::cout << "thread a" <<std::endl;
    }
    void b()
    {
    
    
        cb.wait(); // c->b
        std::lock_guard<std::mutex> lock(printMutex);
        std::cout << "thread b" <<std::endl;
        ba.signal(); // b->a
    }
    void c()
    {
    
    
        dc.wait(); // d->c
        std::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();
    // func
    func();
    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互斥量)

Guess you like

Origin blog.csdn.net/qq_42604176/article/details/121342445