个人主页:Lei宝啊
愿所有美好如期而遇
信号量理论https://blog.csdn.net/m0_74824254/article/details/137863002?spm=1001.2014.3001.5501信号量理论我们前面已经谈过,这里重点介绍信号量的实践,本篇文章将会使用信号量实现基于环形队列的生产者消费者模型。
信号量接口
定义sem_t类型变量
sem_t psem;
sem_t csem;
初始化信号量
第一个参数:指向一个sem_t类型变量的指针,该变量将被初始化为一个信号量
第二个参数:这是一个标志,决定了信号量是在进程内(线程间)共享还是跨多个进程共享。
- 如果
pshared
为 0,则信号量在调用sem_init
的进程内的线程间共享。 - 如果
pshared
非 0(通常设置为 1),则信号量在多个进程间共享。但是,为了跨进程共享,信号量通常位于共享内存区域(如使用mmap
或shmget
和shmat
创建的区域)
第三个参数: 信号量的初始值。这个值必须是非负的。
sem_init(&psem, 0, cap);
sem_init(&csem, 0, 0);
销毁信号量
sem_destroy(&psem);
sem_destroy(&csem);
等待信号量
功能:等待信号量,会将信号量的值减1
//申请资源,sem做--操作
void P(sem_t &sem)
{
sem_wait(&sem);
}
发布信号量
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
//释放资源,sem做++操作
void V(sem_t &sem)
{
sem_post(&sem);
}
基于环形队列的生产消费模型
环形队列
采用数组模拟,用模运算来模拟环形特性。
环形结构起始状态和结束状态都是一样的,不好判断为空或者为满,所以可以通过加计数器或者标记位来判断满或者空。另外也可以预留一个空的位置,作为满的状态,即(tail+1)%N == head。
但是我们有了信号量这个计数器之后,多线程的同步就会简单很多,不需要额外的判断。
实现
#pragma once
#include <pthread.h>
#include <vector>
#include <semaphore.h>
using namespace std;
pthread_mutex_t p_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t c_mutex = PTHREAD_MUTEX_INITIALIZER;
template<class T>
class RingQueue
{
public:
RingQueue(int cap)
:_cap(cap)
,rq(cap)
{
sem_init(&psem, 0, cap);
sem_init(&csem, 0, 0);
}
//申请资源,sem做--操作
void P(sem_t &sem)
{
sem_wait(&sem);
}
//释放资源,sem做++操作
void V(sem_t &sem)
{
sem_post(&sem);
}
void Push(const T& in)
{
P(psem);
pthread_mutex_lock(&p_mutex);
rq[pindex++] = in;
pindex %= _cap;
pthread_mutex_unlock(&p_mutex);
V(csem);
}
void Pop(T& out)
{
P(csem);
pthread_mutex_lock(&c_mutex);
out = rq[cindex++];
cindex %= _cap;
pthread_mutex_unlock(&c_mutex);
V(psem);
}
~RingQueue()
{
sem_destroy(&psem);
sem_destroy(&csem);
}
private:
int _cap;
vector<T> rq; //存放资源
sem_t psem;
sem_t csem;
int pindex = 0;
int cindex = 0;
};
这样,生产者和消费者在环形队列不为空和满时可以并发访问临界资源,即使为空为满,也会有互斥。生产者和生产者之间,消费者和消费者之间也会有很好的并发性,相比于阻塞队列,在访问临界资源的并发性上具有优势,具有更细粒度的并发控制。