• 《Linux从练气到飞升》No.30 深入理解 POSIX 信号量与生产消费模型


    🕺作者: 主页

    😘欢迎关注:👍点赞🙌收藏✍️留言

    🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

    前言

    在多线程编程领域,理解 POSIX 信号量的概念和相关函数是至关重要的。POSIX 信号量作为一种重要的同步原语,可以帮助我们在多线程环境中实现线程之间的协调与同步,从而确保数据的一致性和避免竞争条件的发生。

    本篇博客旨在深入探讨 POSIX 信号量的基本概念和相关函数,帮助读者全面理解这一关键的并发编程工具。通过本文的学习,读者将能够掌握如何灵活地运用 POSIX 信号量来构建并发程序,提高程序的性能和可靠性。让我们一起深入探索 POSIX 信号量的奥秘,为多线程编程的世界增添新的智慧与力量。

    1 POSIX信号量相关概念

    POSIX信号量是什么?

    信号量的本质是一个计数器,是用来描述临界资源有效的计数器

    POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。
    当多个线程想要获取信号量的时候,都会对信号量当中的资源计数器进行减一操作。
    如果初始化信号量的资源计数器的值为1表示当前只有一个资源,这就意味着只有一个线程在同一时刻可以获取到信号量。
    如果想要实现线程同步,初始化信号量的资源计数器的值就不必为1了,它可以根据需要设置

    • 如果大于0则表示还有多少资源可以使用
    • 等于0则表示没有资源可以使用
    • 小于0则表示有多少线程在等待资源。

    2 POSIX信号量相关函数

    1. 信号量初始化
    #include 
    int sem_init(sem_t *sem, int pshared, unsigned int value);
    参数:
    	sem_t: 信号量的类型
    	sem: 传入待要初始化的信号量
    	pshared: 0 表示线程间共享,非0表示进程间共享
    	value:信号量初始值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 信号量销毁
    int sem_destroy(sem_t *sem);
    参数:
    	sem:待销毁的信号量
    
    • 1
    • 2
    • 3
    1. 信号量等待
    功能:等待信号量,会将信号量的值减1
    	int sem_wait(sem_t *sem); //P()
    
    • 1
    • 2
    1. 信号量发布
    功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1int sem_post(sem_t *sem);//V()
    
    • 1
    • 2

    3 基于环形队列的生产消费模型

    • 上一个生产者-消费者的例子是基于queue的,其空间可以动态分配,现在基于固定大小的环形队列重写这个程序(POSIX信号量)。

    image.png

    • 环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态

    image.png
    但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程
    实现代码如下:
    RingQueue.cc

    #pragma
    
    #include
    #include
    #include
    #include
    #include
    
    #define NUM 10
    
    class RingQueue
    {
    private:
        std::vector<int> v;
        int _cap;//容量
        sem_t sem_product;//生产者
        sem_t sem_consume;//消费者
    
        int p_index;//生产者索引
        int c_index;//消费者索引
    
    public:
        RingQueue(int cap=NUM)
            :_cap(cap),v(cap)
        {
            sem_init(&sem_product,0,cap);
            sem_init(&sem_consume,0,0);
            p_index = 0;
            c_index = 0;
        }
        ~RingQueue(){
            sem_destroy(&sem_product);
            sem_destroy(&sem_consume);
        }
    
        void put(const int&in){
            sem_wait(&sem_product);
            v[p_index] = in;
            p_index++;
            p_index = p_index%NUM;
            sem_post(&sem_consume);
        }
    
        void get(int &out){
            sem_wait(&sem_consume);
            out = v[c_index];
            c_index++;
            c_index = c_index%NUM;
            sem_post(&sem_product);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    main.cc

    #include"RingQueue.cc"
    using namespace std;
    
    void* Consumer(void* arg){
        RingQueue *bq = (RingQueue*)arg;
        int data;
        while(1){
            bq->get(data);
            cout<<"I am "<<pthread_self()<<" is consumer : "<<data<<endl;
        }
    }
    
    void* Product(void* arg){
        RingQueue* bq = (RingQueue*)arg;
        srand((unsigned int)time(NULL));
        while(1){
            int data = rand()%100;
            bq->put(data);
            cout<<"I am "<<pthread_self()<<" is product "<<data<<endl;
            sleep(1);
        }
    }
    
    
    int main()
    {
        RingQueue* pq = new RingQueue();
        pthread_t c;
        pthread_t p;
        pthread_create(&c,NULL,Consumer,(void*)pq);
        pthread_create(&p,NULL,Product,(void*)pq);
    
        pthread_join(c,NULL);
        pthread_join(p,NULL);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    makefile

    main:main.cc
    	g++ -o $@ $^ -lpthread
    .PHONY:
    clean:
    	rm -f main
    
    • 1
    • 2
    • 3
    • 4
    • 5

    结果:
    可以观察到生产一个消费一个
    image.png

  • 相关阅读:
    前端——重要 发送表单数据 post
    EtherCAT从站EEPROM分类附加信息详解:TXPDO(输出过程数据对象)
    CC++ socket网络编程扫盲篇
    被骂怕了?微软将允许Windows 11用户一键设置默认浏览器
    Node.js 事件循环
    竹间智能用认知智能为企业的发展提供助力
    利用uni-app 开发的iOS app 发布到App Store全流程
    【论文简述及翻译】Learning for Disparity Estimation through Feature Constancy(CVPR 2018)
    算法 滑动窗口最大值-(双指针+队列)
    深入探究音视频开源库WebRTC中NetEQ音频抗网络延时与抗丢包的实现机制
  • 原文地址:https://blog.csdn.net/m0_67759533/article/details/134365201