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;
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/attemptendeavor/article/details/124002547
内容来源于网络,如有侵权,请联系作者删除!