📘 推荐阅读:《Yocto 项目实战教程:高效定制嵌入式 Linux 系统》
作者:孙杰(Jerry Sun)|电子工业出版社
京东购买链接 👉 https://item.jd.com/15020438.html
信号量原理全解:同步利器与互斥锁的比较
1. 信号量是什么?——本质与核心作用
在并发编程中,“同步”是保证多线程/多进程安全访问共享资源的关键。**信号量(Semaphore)**就是一种“通用的同步与资源计数工具”。
它的本质可以简单理解为:一个可自动递减/递增的计数器,专门用来限制“可用资源的数量”。
信号量通过两个核心操作:
- P操作(等待/获取/Down): 资源计数器减1,若变成负值,阻塞等待。
- V操作(释放/增加/Up): 资源计数器加1,唤醒一个等待者(如果有)。
为什么需要信号量?
在实际开发中,常见如下场景:
- 控制某个资源的最大并发访问数(如数据库连接池、停车场进出闸口)。
- 协调不同线程/进程间的工作流程(如生产者-消费者问题)。
- 实现比互斥锁更灵活的“多资源并发限制”。
2. 信号量的种类与工作机制
2.1 二值信号量(Binary Semaphore)
- 取值仅为0或1,等价于“开关”。
- 用于“互斥”,即保证同一时刻只有一个线程访问资源(本质上就是“互斥锁”)。
2.2 计数信号量(Counting Semaphore)
- 计数可为0~N(N为资源数)。
- 用于控制最多N个线程/进程并发访问某资源。
2.3 工作流程图解
- 初始化信号量,设定资源数目N。
- 每当一个线程需要访问资源时,执行
P操作
(计数-1);若计数<0,则等待。 - 访问结束后执行
V操作
(计数+1);唤醒其它等待线程。
3. 信号量的经典实战例子
3.1 停车场问题(最容易理解)
假设有一个3车位的停车场,5辆车会随机来停车。停车场有空位才能进入,否则必须等待。
代码实现(C语言伪代码,便于理解)
#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
sem_t parking_lot; // 信号量表示可用车位
void* car(void* arg) {
int id = *(int*)arg;
printf("🚗 车%d等待进入停车场...\n", id);
sem_wait(&parking_lot); // 占用一个车位
printf("✅ 车%d已进入停车场\n", id);
sleep(2); // 模拟停车过程
printf("🅿️ 车%d准备离开停车场\n", id);
sem_post(&parking_lot); // 释放一个车位
return NULL;
}
int main() {
sem_init(&parking_lot, 0, 3); // 初始化车位为3
pthread_t threads[5];
int ids[5];
for(int i=0; i<5; i++) {
ids[i] = i+1;
pthread_create(&threads[i], NULL, car, &ids[i]);
sleep(1); // 模拟不同时间到达
}
for(int i=0; i<5; i++) {
pthread_join(threads[i], NULL);
}
sem_destroy(&parking_lot);
return 0;
}
效果说明
- 最多只有3辆车能同时进入停车场,超过的必须等有车离开。
- 这就是计数信号量的最直观应用!
3.2 生产者-消费者模型(同步信号量典范)
假设有一个缓冲区(长度N),生产者生产产品放进去,消费者取出消费。
- 如果缓冲区满,生产者必须等。
- 如果缓冲区空,消费者必须等。
这可以用两个信号量控制:
empty
:表示“剩余空位数”full
:表示“已用空间数”
略去代码(如需详细实现可补充),原理同上。
4. 信号量 VS 互斥锁 VS 其他同步工具
4.1 互斥锁(Mutex)
- 只能用于“互斥”,即同一时刻仅允许一个线程进入临界区。
- 本质上等同于“二值信号量”。
- 通常只用于线程同步,不能用于资源计数。
4.2 信号量
- 既能实现“互斥”,又能“资源并发控制”。
- 适合需要“允许N个线程同时访问”场景。
- 可用于线程间/进程间同步(POSIX信号量分线程/进程作用域)。
4.3 条件变量(Condition Variable)
- 主要用于“等待某个条件达成”,本身不做资源计数。
- 常与互斥锁联合使用。
4.4 关键对比表
同步机制 | 主要用途 | 可控制资源数 | 典型场景 | 优缺点 |
---|---|---|---|---|
互斥锁 | 互斥保护 | 1 | 只允许一个访问临界区 | 实现简单、效率高 |
信号量 | 互斥或资源同步 | N(N≥1) | 资源池、连接池等 | 用途广泛、灵活 |
条件变量 | 条件同步/事件通知 | 不适合 | 队列、事件驱动等 | 适合等待/唤醒 |
5. 场景选择建议
-
只需要保证唯一访问(如线程安全操作)?
—— 用互斥锁,最简单最高效。 -
要限制多并发(如固定数量资源池)?
—— 用信号量,计数灵活。 -
复杂的条件等待/多步同步?
—— 用条件变量配合互斥锁。
6. 小结
- 信号量是比互斥锁更灵活的同步机制,本质是一个原子计数器,可实现“多资源并发限制”。
- 互斥锁适用于“唯一访问”,信号量适用于“多资源同步”。
- 合理选择同步工具,可极大简化并发代码设计,提升安全性和效率。
推荐阅读和练习
- 多线程同步实战:用信号量管理“打印机池”、“停车场”、“数据库连接池”等资源。
- 尝试将“互斥锁”改为“二值信号量”,感受本质一致性。
- 多了解下条件变量、读写锁等高级同步机制。
📘 推荐阅读:《Yocto 项目实战教程:高效定制嵌入式 Linux 系统》
作者:孙杰(Jerry Sun)|电子工业出版社
京东购买链接 👉 https://item.jd.com/15020438.html