Linux多进程篇【4】——POSIX信号量

POSIX信号量

POSIX信号量和SystemV信号量作用相同,都是用于同步操作,达到无冲突访问资源目的。但POSIX可以用于线程间同步

概念

  • 信号量本质就是一把计数器,描述临界资源中资源数目的大小,即最多有多少资源能分配给线程。
  • 每个线程想访问临界资源,都得先申请信号量资源
  • 如果临界资源可以被划分为一个一个的小资源,我们也有可能让多个线程同时访问临界资源的不同区域,从而实现并发
    总的来说,信号量可以看作一把计数器,对信号量的合理使用可以达到对临界资源的预定目的。

操作

既然说信号量可以看作是一个计数器,那么我们先来模拟看看信号量应该是怎样工作的。

start:
lock();
if (count <= 0){
	// 挂起
	unlock();
	goto start;
} else{
	count--;
}
unlock();

lock();
count++;
unlock();

初始化

#include <semaphore.h>
// pshared:0表示线程间共享,非零表示进程间共享
// value:信号量初始值
int sem_init(sem_t *sem, int pshared, unsigned int value);

销毁

int sem_destroy(sem_t *sem);

等待

功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem); 

发布

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t *sem);

环形队列

在这里插入图片描述

基本原理

  • 生产者和消费者在开始的时候指向同一位置
  • 生产者和消费者在队列满的时候指向同一位置
  • 当队列不为空或满的时候生产者和消费者一定指向不同的位置,此时生产和消费可以并发执行

实现思想

对于生产者,主要关心空的位置

  • 对于消费者,主要关心数据资源
  • 规则1:生产者不能把消费者套一圈
  • 规则2:消费者不能超过生产者
  • 规则3:当指向同一位置的时候,要根据空、满的状态来判定谁先执行
  • 除此之外生产和消费可以并发执行

结合环形队列和信号量编写生产消费模型

/* 跟之前写的queue生产消费模型一样我们
仍然创建两个进程分别代表生产者和消费者,
他们分别进行生产和消费操作*/
int main()
{
    srand((long long)time(nullptr));
    RingQueue<int>* rq = new RingQueue<int>();
    
    pthread_t c, p;
    pthread_create(&c, nullptr, consumer, (void*)rq);
    pthread_create(&p, nullptr, producter, (void*)rq);

    pthread_join(c, nullptr);
    pthread_join(p, nullptr);

    return 0;
}

消费者和生产者的行为

void* consumer(void* args)
{
    RingQueue<int>* rq = (RingQueue<int>*)args;
    while (true)
    {
        int data = 0;
        rq->Pop(&data);
        std::cout << "消费数据是: " << data << std::endl;
    }
}

void* producter(void* args)
{
    RingQueue<int>* rq = (RingQueue<int>*)args;
    while (true)
    {
        int data = rand() % 20 + 1;
        std::cout << "生产数据是: " << data << std::endl;
        rq->Push(data);
        sleep(1);
    }
}

我们发现消费者和生产者行为的核心就是Pop和Push,因此接下来的代码将实现这两个函数

ring_queue.hpp

#pragma once

#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

namespace ssj_ring_queue
{
    const int g_cap_default = 10;
    template <class T>
    class RingQueue
    {
    private:
        pthread_mutex_t _mtx_p;
        pthread_mutex_t _mtx_c;
        std::vector<T> _ring_queue;
        int _cap;
        // 生产者关心空位资源
        sem_t _blank_sem;
        // 消费者关心数据资源
        sem_t _data_sem;

        int _c_step; // 消费者位置
        int _p_step; // 生产者位置
    public:
        RingQueue(int cap = g_cap_default)
            : _ring_queue(cap), _cap(cap)
        {
            pthread_mutex_init(&_mtx_p, nullptr);
            pthread_mutex_init(&_mtx_c, nullptr);
            sem_init(&_blank_sem, 0, cap);
            sem_init(&_data_sem, 0, 0);
            _c_step = _p_step = 0;
        }
        ~RingQueue()
        {
            sem_destroy(&_blank_sem);
            sem_destroy(&_data_sem);
            pthread_mutex_destroy(&_mtx_p);
            pthread_mutex_destroy(&_mtx_c);
        }

    public:
        void Push(const T &in)
        {
            // 生产接口
            sem_wait(&_blank_sem);
            pthread_mutex_lock(&_mtx_p);
            _ring_queue[_p_step] = in;
            _p_step++;
            _p_step %= _cap;
            pthread_mutex_unlock(&_mtx_p);
            sem_post(&_data_sem);
        }

        void Pop(T* out)
        {
            // 消费接口
            sem_wait(&_data_sem);
            pthread_mutex_lock(&_mtx_c);
            *out = _ring_queue[_c_step];
            _c_step++;
            _c_step %= _cap;
            pthread_mutex_unlock(&_mtx_c);
            sem_post(&_blank_sem);
        }
    };
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JayceSun449

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值