信号量
- 本质:计数器+pcb等待队列;
- 作用:实现进程或线程间的同步与互斥;
- P操作:计数-1;判断计数<0 ,则阻塞进程或线程(将其加入pcb等待队列)
- V操作:计数+1;唤醒pcb等待队列上的一个进程或线程;
- 同步的实现:通过计数器对资源进行计数;获取资源前进程P操作,合理则获取,不合理则阻塞。产生一个资源,则进行一次V操作。
- 互斥的实现:计数最大为1,表示只有一个资源,访问前P操作,访问完进行V操作;
操作接口:
- 定义信号量:
sem_t sem;
- 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
· sem:信号量变量;
· pshared:0-线程间 / !0-进程间;
· value:初值;
· 返回值:成功返回0,失败返回-1; - P操作:
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- V操作:
int sem_post(sem_t *sem);
- 销毁信号量:
int sem_destroy(sem_t *sem);
信号量与条件变量的区别与联系:
- 都是用于实现同步
- 条件变量的使用中条件是否满足需要我们自己判断,信号量不需要;
- 条件变量的使用需要搭配互斥锁;信号量不需要
使用信号量实现生产者与消费者模型:
class RingQueue{
std::vector<int> _arr;
int _capacity;
int _step_read;
int _step_write;
sem_t _sem_lock;//用于实现互斥的信号量
sem_t _sem_idle;//对于生产者-空闲节点是资源
sem_t _sem_data;//对于消费者-数据节点是资源
};
#include <cstdio>
#include <vector>
#include <semaphore.h>
#include <pthread.h>
class RingQueue{
private:
std::vector<int> _arry;
int _capacity;
int _step_read;
int _step_write;
sem_t _sem_lock;//用于实现互斥的信号量
sem_t _sem_idle;//对于生产者-空闲节点是资源
sem_t _sem_data;//对于消费者-数据节点是资源
public:
RingQueue(int cap=5):_capacity(cap), _arry(cap),
_step_read(0), _step_write(0) {
//sem_init(信号量,标志-0线程-1进程, 初值)
sem_init(&_sem_lock, 0, 1);
sem_init(&_sem_idle, 0, cap);
sem_init(&_sem_data, 0, 0);
}
~RingQueue() {
sem_destroy(&_sem_data);
sem_destroy(&_sem_idle);
sem_destroy(&_sem_lock);
}
bool Push(int data) {
sem_wait(&_sem_idle);//判断有没有空闲节点
sem_wait(&_sem_lock);//加锁
_arry[_step_write] = data;
_step_write = (_step_write + 1)%_capacity;
sem_post(&_sem_lock);//解锁
sem_post(&_sem_data);//数据节点+1唤醒消费者
return true;
}
bool Pop(int *data) {
sem_wait(&_sem_data);//数据节点数-1,判断是否合理
sem_wait(&_sem_lock);//加锁
*data = _arry[_step_read];
_step_read = (_step_read + 1)%_capacity;
sem_post(&_sem_lock);//解锁
sem_post(&_sem_idle);//空闲节点数+1唤醒生产者
return true;
}
};
void *productor(void *arg)
{
RingQueue *q = (RingQueue*)arg;
int i = 0;
while(1){
q->Push(i);
printf("生产者入队数据:%d\n", i++);
}
return NULL;
}
void *customer(void *arg)
{
RingQueue *q = (RingQueue*)arg;
while(1) {
int data;
q->Pop(&data);
printf("消费者出队数据: %d\n", data);
}
return NULL;
}
int main (int argc, char *argv[])
{
RingQueue q;
pthread_t ptid[4], ctid[4];
int ret;
for (int i = 0; i < 4; i++) {
ret = pthread_create(&ptid[i], NULL, productor, &q);
if (ret != 0) {
printf("create thread error\n");
return -1;
}
}
for (int i = 0; i < 4; i++) {
ret = pthread_create(&ctid[i], NULL, customer, &q);
if (ret != 0) {
printf("create thread error\n");
return -1;
}
}
for (int i = 0; i < 4; i++) {
pthread_join(ptid[i], NULL);
pthread_join(ctid[i], NULL);
}
return 0;
}