POSIX信号量(基于环形队列的生产消费模型)

文章目录

  • POSIX信号量
    • 是什么?
    • 信号量的意义?
    • 怎么用?
  • 基于环形队列的生产消费模型


POSIX信号量

是什么?

信号量(也叫信号灯),本质是一个在描述临界资源中,有效资源个数的计数器。

如果把临界区划分成多个块儿,那么多个线程可以同时进入临界区访问不同的块。
之前的锁,是我们认为临界资源只有一块。

信号量可以保证让多个线程同时进入临界区,访问临界资源的不同块。

申请信号量,把计数器--叫做P操作;释放信号量,计数器++叫做V操作。

要保证所有的线程都得看到信号量,所以信号量本身就是临界资源,因此得保证它的安全。
因此PV操作一定是原子的。

信号量的意义?

临界资源可以划分成多块。
这样就可以并行的访问临界资源,提高效率。

怎么用?

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

  • 初始化信号量
#include 
int sem_init(sem_t *sem, int pshared, unsigned int value);

pshared:0表示线程间共享,非零表示进程间共享
value:信号量初始值

  • 销毁信号量
int sem_destroy(sem_t *sem);
  • 等待信号量 - P操作:会将信号量的值减1
int sem_wait(sem_t *sem); 
  • 发布信号量 - V操作:会将信号量的值加1
int sem_post(sem_t *sem);

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

环形队列可以抽象成数组,用模运算来模拟环状特性。
POSIX信号量(基于环形队列的生产消费模型)_第1张图片
生产者和消费者不指向同一个位置时,队列不满不空;而指向同一个位置时,队列是满或空的状态。

也就是说,下标不同时,它们并行(同步);相同时,它们串行。(互斥)

该模型用信号量维持了生产者和消费者的同步与互斥。

  • RingQueue.hpp
#pragma once

#include 
#include 
#include 
#include 

#define NUM 10

class RingQueue{
     
    private:
        void P(sem_t &s) // 引用
        {
     
            sem_wait(&s); // 取地址
        }
        void V(sem_t &s)
        {
     
            sem_post(&s);
        }
    public:
        RingQueue(int _cap = NUM):max_cap(_cap), v(_cap)
        {
     
            sem_init(&sem_space, 0, max_cap); // 0 代表线程间共享,max_cap信号量初始值
            sem_init(&sem_data, 0, 0); // 数据资源最初为0
            c_index = 0;// 默认消费者从0开始走
            p_index = 0;// 默认生产者从0开始走
        } 
        void Put(const int &in)
        {
     
            P(sem_space); // 生产者,申请格子资源
            v[p_index] = in; // 生产
            p_index++;
            p_index %= max_cap;
            V(sem_data); // 多了一份数据资源
        }
        void Get(int &out)
        {
     
            P(sem_data); // 消费者,申请数据资源
            out = v[c_index]; // 消费
            c_index++;
            c_index %= max_cap;
            V(sem_space); // 消费完就多了一份格子资源
        }
        ~RingQueue()
        {
     
            sem_destroy(&sem_space);
            sem_destroy(&sem_data);
            c_index = 0;
            p_index = 0;
        }
    private:
        int max_cap;
        std::vector<int> v;
        sem_t sem_space; // 生产者关心的格子资源
        sem_t sem_data;  // 消费者关心的数据资源

        int c_index;  // 决定了它使用临界资源的哪一份
        int p_index;
};

  • main.cc
#include"RingQueue.hpp"

void* producer(void* ring_queue)
{
     
    RingQueue* rq = (RingQueue*)ring_queue;

    while(true)
    {
     
        int count = rand()%100 + 1;
        sleep(1);
        rq->Put(count); //生产
        std::cout<< "producer is producing" << std::endl;
    }
}
void* consumer(void* ring_queue)
{
     
    RingQueue* rq = (RingQueue*)ring_queue;
    while(true)
    {
     
        int data = 0;
        rq->Get(data); // 消费
        std::cout<< "consumer data: #" << data << std::endl;
    }
}

int main()
{
     
    pthread_t p,c;
    RingQueue *rq = new RingQueue();

    pthread_create(&p, nullptr, producer, rq);
    pthread_create(&c, nullptr, consumer, rq);

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

    delete rq;
    return 0;
}
  • Makefile
testRingQueue:main.cc
	g++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f testRingQueue

你可能感兴趣的:(多线程,linux)