信号量
信号量的同步与互斥实现
作用:实现线程或进程之间的同步与互斥
本质:计数器+等待队列+等待与唤醒的功能接口
场景:用于实现进程/线程间的同步与互斥
信号量实现同步:通过自身的计数器进行资源计数,对临界资源访问之前先访问信号量,通过计数判断是否有资源能够访问,若不能访问( 计数<=0 没有资源),则等待,并且计数-1,(如果没有资源了,资源为0,等待并且计数-1,变成-1),当信号量计数器为负值,就说明有多少线程正在等待;若可以访问-计数>0,则计数-1,直接访问;其它线程生产资源促使条件满足后,则判断若计数>0,计数+1(表示没人等待,只把计数+1就可以了,说明此时已经有资源了),否则若计数<0,则唤醒一个等待队列上的线程,并计数+1.(<0说明有人在等待,计数+1,就说明等待的人少了一个)
信号量实现互斥:
只需要将计数维持在0/1之间就可以实现互斥
信号量与条件变量实现同步的区别:
条件变量实现同步,没有提供给用户条件判断的功能,是需要用户自己来判断的而信号量本身有计数器,资源计数,有没有资源都是通过计数器来进行资源计数的,通过计数器,我们就能知道有没有资源,能不能访问,跟信号量最大的区别就是条件变量通过自身的计数来实现条件判断。
信号量通过自身条件判断实现同步,条件变量是通过用户进行条件判断;并且条件变量使用的是外部用户的条件判断,必须是搭配互斥锁一起使用,而信号量,是通过自身的条件判断,在内部保证了操作的原子性。
信号量的接口函数:
1. 定义信号量: sem_t类型 (sem_t类型的结构体中肯定有 计数器,阻塞队列,等待与唤醒的接口)
2. 初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
信号量变量,(sem_t 结构体至少要有计数器,等待队列,保证计数安全内部有其它的锁)
pshared :标志位,决定了当前的信号量用与进程间还是线程间,0-线程间 ,!0-进程间
(因为是一个库,线程间用全局变量就可以完成,用于进程间(进程间相互独立),实际上申请了一块共享内存,在共享内存中包含了计数器 和共享队列)
信号量的初值(由用户根据资源而定)如果定义的是互斥锁的话,那么初始值就是1,因为互斥是由0和1控制的。
成功0 失败-1
4. 在访问临界资源之前,先访问信号量,判断是否访问: 计数-1
条件判断(有没有资源自给判断)+等待
int sem_wait(sem_t *sem) ;//若计数<= 0, 则-1后阻塞;否则-1后立即返回 --阻塞函数
trywait(sem_t *sem)-- 通过自身计数判断是否满足访问条件,不满足则立即报错返回 EINVAL
timedwait(sem_t *sem,const struct timespec* abs_timeout)-- 限制时长的阻塞,超时后报错返回 ETIMEDOUT
5. 促使访问条件满足 + 1, 唤醒阻塞线程/进程
int sem_post(sem_t *sem); //唤醒信号量等待队列上的线程 且计数+1
6. 销毁信号量
int sem_destroy(sem_t * sem); //销毁信号量
注意: 一个信号量只有一个等待队列,所以我们用信号量实现生产者与消费者模型的时候,需要定义两个信号量实现同步,每一个信号量需要等待在每一个不同的等待队列上。
信号量如何实现同步与互斥
信号量实现同步
厨师做饭的例子:
sem_t _sem;
void * food()
{
int i = 0;
while(1)
{
sem_wait(&_sem); // 没有资源则阻塞等待,有资源则-1访问资源
printf("%d delicious ~~\n",i++);
}
return NULL;
}
void* cook()
{
while(1)
{
printf("cook ~~\n");
sleep(1);
sem_post(&_sem); //资源+1
}
return NULL;
}
本文详细介绍了Linux中信号量的概念及其在多线程同步与互斥中的应用。通过信号量的计数器和等待队列,可以实现线程间的同步与互斥。同时对比了信号量与条件变量的区别,指出信号量自带条件判断功能,而条件变量需要配合互斥锁使用。文中还提供了使用信号量实现生产者消费者模型的代码示例,强调了在实现过程中对临界资源的保护和正确使用信号量的重要性。
最低0.47元/天 解锁文章
2743

被折叠的 条评论
为什么被折叠?



