一、前言
前面我们学完了 互斥锁、读写锁、条件变量。
我们知道:
- 互斥锁保证同一时刻只有一个线程访问临界资源,保证数据安全;
- 条件变量让线程不再空轮询、主动阻塞休息,极大节省 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这种原子操作。
本文深入解析Linux系统编程中的POSIX信号量,重点阐述其如何通过内置计数器解决条件变量的‘唤醒丢失’问题。详细对比无名信号量(sem_t,用于线程间同步)与有名信号量(用于进程间同步)的API、初始化参数及使用约束,并强调sem_wait/sem_post的原子性本质,指出sem_getvalue不可用于同步逻辑判断。
921

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



