1、信号量概念
-
进化版本的互斥锁,在互斥锁中,我们用的是两个线程,而这个信号量是可以N个线程。
-
本质是一个计数器
- 信号量大于0,则信号量- -
- 信号量等于0,造成阻塞
-
既可以在线程中使用,也可以在进程中使用
-
他和管道不同在于,他主要是保护数据,而不是传输数据,他是为了在某一个时刻只有一个线程(或进程)来对数据进行操作。
注意:信号量和信号是两个不同的关系,要做一个区分。
2、信号量原语
sem_t 建立信号量
sem_init 初始化信号量
sem_wait 阻塞
sem_trywait 尝试阻塞
sem_timedwait 计时阻塞
sem_post 唤醒信号量
sem_destroy 销毁信号量
- 头文件:#include <semaphore.h>
- 返回值:都是成功为0,失败位-1
2.1、创建信号量
sem_t类型,用来创建信号量
- 示例:sem_t blank_number这样就创建了一个 名为 blank_number的信号量
2.2、初始化信号量
- 函数:int sem_init(sem_t *sem, int pshared, unsigned int value);
- 参数解析:
- 参数1:传入一个信号量结构体进行初始化
- 参数2:此参数如果为0表示当前进程的局部信号量,否则,其他进程就能够共享这个信号量(说白了就是,如果为0用于线程,不为零用于进程)
- 参数3:赋值给信号量对象的一个初始值,这个初始值决定作用信号量的线程(进程)个数
- 作用:对给定信号量对象进行初始化。
2.3、信号量唤醒
- 函数:int sem_post(sem_t *sem);
- 参数解析:传入一个信号量结构体进行唤醒
- 作用:将信号量++,同时唤醒阻塞在信号量的线程,相当于unlock
2.4、信号量阻塞
2.4.1、普通阻塞
函数:int sem_wait(sem_t *sem);
参数解析:传入一个信号量结构体,来改变对象的值
作用:把信号量- -,如果信号量的值大于零,那么解析继续进行,函数立即返回。 如果信号量当前的值为零,那么调用就会阻塞,直到可以执行减量操作(例如,信号量值上升到零以上) ,或者信号中断调用
2.4.2、尝试阻塞
函数:int sem_trywait(sem_t *sem);
参数解析:传入一个信号量结构体,来改变对象的值
作用:与 sem_wait ()相同,只是如果不能立即执行递减操作,则调用返回一个错误(errno set to eagain) ,而不是阻塞。
2.4.2、计时阻塞
- 函数:int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- 参数解析:
- 参数1:传入一个信号量结构体,来改变对象的值
- 参数2:传入 在一个timespec结构体来设定等待是时间
- 作用:sem wait ()相同,只是 abs_timeout指定了一个限制,如果不能立即按形式减少调用,则调用应该阻塞的时间量,超过时间返回错误。
- 时间结构体
struct timespec
{
time_t tv_sec; /* 秒*/
long tv_nsec; /* 纳秒[0 … 999999999] */
};
示例代码——生产者消费者模型
#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>
//创建一个可容纳5个物体的空间
#define NUM 5
int queue[NUM];
//创建两个信号变量消费者和生产者
//可以理解成blank_number为剩余空间
//product_number为已占用的空间
sem_t blank_number, product_number;
//生产者创建物体
void *producer(void *arg)
{
int p = 0;
while (1)
{
//对于生产者来说,生产一个物品,剩余空间blank_number要减少,已占用空间product_number要增加
//剩余空间blank_number减少
sem_wait(&blank_number);
queue[p] = rand() % 1000 + 1;
printf("Produce %d\n", queue[p]);
//占用空间product_number增加
sem_post(&product_number);
//p取模的话就能把p限制在1-5一直循环
p = (p+1)%NUM;
//随机睡眠
sleep(rand()%5);
}
}
//消费者消费物体
void *consumer(void *arg)
{
//对于消费者来说,剩余空间要增加,占用空间要减少,因为我这个消费者消费了一个物品
int c = 0;
while (1)
{
//已占用空间product_number减少
sem_wait(&product_number);
printf("Consume %d\n", queue[c]);
queue[c] = 0;
//剩余空间blank_number增加
sem_post(&blank_number);
c = (c+1)%NUM;
sleep(rand()%5);
}
}
int main(void)
{
pthread_t pid, cid;
//初始化两个信号量
sem_init(&blank_number, 0, NUM);
sem_init(&product_number, 0, 0);
//线程创建
pthread_create(&pid, NULL, producer, NULL);
pthread_create(&cid, NULL, consumer, NULL);
//线程回收
pthread_join(pid, NULL);
pthread_join(cid, NULL);
//信号量销毁
sem_destroy(&blank_number);
sem_destroy(&product_number); return 0;
}