Linux信号量

x33g5p2x  于2022-04-07 转载在 Linux  
字(2.7k)|赞(0)|评价(0)|浏览(400)

POSIX信号量

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

先举个现实生活中的例子:看电影时我们要买票,我们买完票觉得那个座位就属于我们的,现实生活中,有很多场景是有"预定"机制,通过预定可以保证一人一座,电影院的售票系统卖出去的票不能超过电影院的承受能力

信号量(信号灯),本质是一个计数器,描述临界资源中有效个数的计数器,保护临界资源的安全性

比如count = 5;获取到一个临界资源count–,释放时count++;这里的count–是P();操作,count++是V();操作,信号量本身也是临界资源,因为要被每个线程看到,所以PV操作是原子的

伪代码:

struct sem
{
    int count;
    mutex lock;
    wait_queue *head;
}
P()
{
    lock();
    if(count > 0)
        count--;
    else
        wait;
    unlock();
}

那么信号量怎么用呢?

信号量相关接口

初始化信号量

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

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

销毁信号量

int sem_destroy(sem_t *sem);

释放信号量自己占用的一切资源,满足条件成功返回0,否则返回-1且置errno为EBUSY

等待信号量

功能:等待信号量,会将信号量的值减1

int sem_wait(sem_t *sem); //P()

操作成功返回0,失败则返回-1且置errno

发布信号量

功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。

int sem_post(sem_t *sem);//V()

操作成功返回0,失败则返回-1且置errno

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

环形结构可以由数组来模拟,采用数组模拟,用模运算来模拟环状特性

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

但是我们现在有信号量这个计数器,就很简单的进行多线程间的同步过程。

consumer关心的是数据数量,productor关心的是空格子的数量

怎么保证数据不被覆盖或者不读取废弃数据:
快的人不能把慢的人套一个圈,慢的人不能超过快的人

大部分情况下,生产者和消费者不会访问同一个位置,什么情况下,会访问同一个位置?

快的人把慢的人套一个圈时,以及慢的人刚到快的人这里

数据:sem_data(数据信号量)

格子:sem_blank(格子信号量)

对于消费者:申请资源:P(sem_data)–,释放资源:V(sem_blank)++

对于生产者:申请资源:P(sem_blank)–,释放资源:V(sem_data)++

下面我们来完成基于环形队列的生产消费模型代码的编写:

Makefile的编写:

main:main.cc
	g++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f main

RingQueue.hpp

#pragma once
#include<semaphore.h>
#include<vector>
#include<iostream>
#define NUM 10
class RingQueue
{
private:
    std::vector<int> v;//数组模拟环形队列
    int _cap;
    sem_t sem_blank;//生产者
    sem_t sem_data;//消费者
    
    int c_index;
    int p_index;
private:
    void P(sem_t &s)
    {
        sem_wait(&s);
    }
    void V(sem_t &s)
    {
        sem_post(&s);
    }
public:
    RingQueue(int cap = NUM):_cap(cap),v(cap)
    {
        sem_init(&sem_blank,0,_cap);
        sem_init(&sem_data,0,0);
        c_index = 0;
        p_index = 0;
    }
    void Get(int& out)
    {
        P(sem_data);//没有时会等待
        //消费
        out = v[c_index];
        c_index++;
        c_index %= _cap;//p_index等于数组的容量时回到0下标
        V(sem_blank);
    }
    void Put(const int &in)
    {
        P(sem_blank);
        //生产
        v[p_index] = in;
        p_index++;
        p_index %= _cap;//p_index等于数组的容量时回到0下标
        V(sem_data);
    }
    ~RingQueue()
    {
        sem_destory(&sem_blank);
        sem_destory(&sem_data);
        c_index = 0;
        p_index = 0;
    } 
};

main.cc

#include"RingQueue.hpp"
void* comsumer(void *ring_queue)
{
    RingQueue *rq = (RingQueue*)ring_queue;
    while(true)
    {
        int data = 0;
        rq->Get(data);
        std::cout<<"comsumer done ...#" << data<<std::endl;
    }
}
void* productor(void *ring_queue)
{
    RingQueue *rq = (RingQueue*)ring_queue;
    int count = 100;
	while(true)
    {
        rq->Put(count);
        count++;
        if(count > 110)
        {
            count = 100;
        }
        std::cout<<"productor done: ..."<<std::endl;
    }
}
int main()
{
    
    pthread_t c,p;
    RingQueue *rq = new RingQueue();
    pthread_create(&c,nullptr,comsumer,rq);
    pthread_create(&c,nullptr,productor,rq);
    
    pthread_join(c,nullptr);
    pthread_join(p,nullptr);
    
    delete rq;
    return 0;
}

相关文章