基于Posix信号量实现的环形生产者消费者模型
posix信号量
- 作用
可以完成线程间与进程间的同步与互斥
- 本质
资源计数器 + PCB等待队列 + 提供等待和唤醒的接口
在条件变量中,我们了解到条件变量是实现线程间同步功能的一种方式。而posix信号量和条件变量相比,多了一个资源计数器。
资源计数器的作用就是用来对临界资源进行比较,posix信号量通过判断自身的资源计数器的情况,来得到当前资源是否可用的信息:
- 可用:则对临界资源进行访问
- 不可用:则进行阻塞等待,直到被唤醒接口唤醒。
操作接口
定义
sem_t
eg:sem_t sem;
初始化接口
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
- sem:传入信号量的地址
- pshared:表示当前信号量表示的内容是线程间还是进程间
0 --》 表示线程间
1 --》 表示进程间
当使用sem_init()
函数初始化信号量为进程间的时候,会在共享内存中开辟一段共享内存,用来保存信号量的数据结构,其中包括资源计数器和PCB等待队列。所以调用唤醒或者等待接口的本质就是通过操作共享内存实现了不同进程之间的通信,进而实现不同进程间的同步与互斥。
而在线程之间,一般就把这个信号量设置为全局变量或者类的私有成员变量,就可以通过操作这个变量来实现不同线程之间的同步和互斥。
- value:实际的资源数量,用于初始化信号量当中的资源计数器,表示当前一共有value个资源
等待接口
#include <semaphore.h>
//阻塞等待
int sem_wait(sem_t *sem);
//非阻塞等待
int sem_trywait(sem_t *sem);
//带有时间的等待
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- 如果判断当前信号量资源计数器中的值大于0,则能够成功获取信号量;如果资源计数器小于等于0,就说明没有资源,则阻塞该线程
唤醒接口
#include <semaphore.h>
int sem_post(sem_t *sem);
- 作用:用来发布信号量,告诉程序当前资源使用完成,需要归还资源或者让生产者重新生产一个资源,并对信号量中的资源计数器进行+1操作,然后唤醒PCB等待队列中的线程。
销毁接口
#include <semaphore.h>
int sem_destroy(sem_t *sem);
如何保证同步&互斥
- 同步
在初始化工作的时候,根据我们制定的资源的数量来初始化posix信号量中的资源计数器
- 互斥
互斥的本质就是让资源计数器只有两个取值,一个是可以用,一个是不可以用。
所以我们就可以初始化信号量中的资源计数器的值为1,这样就保证了1为资源可以用,0为资源不可以用。
基于Posix信号量实现的环形生产者消费者模型
使用的数据结构
一个用数组模拟的环形队列
这个队列需要满足先进先出的特性
实现操作
- 前后指针,一个是生产者指针,一个是消费者指针。当两个指针指向同一位置的时候,表示表示当前队列为空;而当生产者指针的下一个位置 = 消费者指针的位置时,表示队列已满。
- 计算下一个位置的方式
(当前位置 + 1) % 数组容量
- 同步
在生