【Linux系统编程】多线程并发:POSIX 信号量(无名&有名)+信号量版生产者-消费者

本文深入解析Linux系统编程中的POSIX信号量,重点阐述其如何通过内置计数器解决条件变量的‘唤醒丢失’问题。详细对比无名信号量(sem_t,用于线程间同步)与有名信号量(用于进程间同步)的API、初始化参数及使用约束,并强调sem_wait/sem_post的原子性本质,指出sem_getvalue不可用于同步逻辑判断。
一、前言
 
前面我们学完了 互斥锁、读写锁、条件变量。
 
我们知道:
 
- 互斥锁保证同一时刻只有一个线程访问临界资源,保证数据安全;
​
- 条件变量让线程不再空轮询、主动阻塞休息,极大节省 CPU 资源。
 
但条件变量存在一个致命缺陷:
 
条件变量没有计数器!
 
如果  pthread_cond_signal  发出唤醒信号时,没有线程正在 wait 阻塞,本次唤醒会直接丢失、永久失效。
后续线程再来等待时,不会收到任何之前的通知,程序逻辑极易错乱。
 
为了解决 “唤醒丢失” 问题,Linux 引入了 POSIX 信号量。
 
信号量自带计数器,事件可以被保存、堆积、不会丢失,是比条件变量更稳定的同步工具。
 
二、什么是 POSIX 信号量
 
信号量本质是一个自带计数器的同步工具。
 
核心只有两种操作:
 
- P 操作(sem_wait):申请资源,计数器--,资源不足则阻塞
​
- V 操作(sem_post):释放资源,计数器++,唤醒阻塞线程
 
根据使用场景分为两类:
 
1. 无名信号量 sem_t:内存级信号量,多用于 线程间同步
​
2. 有名信号量:文件级信号量,多用于 进程间同步
三、无名信号量API

注意初始化信号量的参数:

int sem_init(sem_t *sem, int pshared, unsigned int value);

1)要想在进程间共享,无名信号量必须位于共享内存区域内。

2)对于线程间共享的信号量,线程组退出了,无名信号量也就不复存在了。

3)无名信号量初始化以后,就可以像操作有名信号量一样操作无名信号量了。

4)value:信号量初始值

                value=1:二值信号量,类似互斥锁    value>1:计数信号量

5)pshared: 用于声明信号量是在线程间共享还是在进程间共享。

              0 表示在线程间共享,非零值则表示信号量将在进程间共享。

6)return: 成功返回0,失败返回-1

四、有名信号量API

五、示例代码

结果发现,生产者线程连续生产了3个产品之后,full_num的值为2而不是设想的3,原因是并发编程中典型的“竞态时序”问题:

在多线程环境下,消费者A执行完sem_post(信号量+1)之后,还没有来得及printf sem_getvalue这一步,cpu的时间片又立马被线程B抢走了,线程B执行sem_wait(信号量-1),这时候线程A再来printf,就是“过时”的数据了。

这个现象说明:sem_getvalue这种函数,绝对不能用来做同步控制的逻辑判断(它无法严格追踪实时状态)。真正的同步,必须完全依赖sem_wait和sem_post这种原子操作。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值