在多线程/多任务系统中,资源竞争会导致数据不一致或系统死锁,信号量(Semaphore)与互斥锁(Mutex)通过同步机制协调共享资源的访问,其核心原理与实现如下:
1. 互斥锁:独占式资源访问
互斥锁确保同一时刻仅一个执行单元(线程/任务)访问资源,适用于严格互斥场景(如修改全局变量)。
操作流程:pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 初始化锁
void critical_section() {
pthread_mutex_lock(&lock); // 获取锁(阻塞或失败返回)
// 访问共享资源(如写全局变量)
pthread_mutex_unlock(&lock); // 释放锁
}
关键特性:
所有权:锁的持有者必须负责释放,不可跨线程解锁。
优先级继承:部分RTOS(如FreeRTOS)自动提升低优先级任务的优先级,避免优先级反转。
2. 信号量:计数型资源池管理
信号量通过计数器管理资源池,允许指定数量的执行单元同时访问资源,适用于流量控制或生产者-消费者模型。
二进制信号量(类似互斥锁):sem_t sem;
sem_init(&sem, 0, 1); // 初始值1(可用资源数)
void access_resource() {
sem_wait(&sem); // 计数器减1(若为0则阻塞)
// 使用资源
sem_post(&sem); // 计数器加1,唤醒等待者
}
计数信号量:sem_init(&sem, 0, 5); // 允许5个线程并发访问
3. 典型应用场景
硬件外设竞争:
多个任务访问同一SPI总线时,使用互斥锁确保单次独占通信。
// FreeRTOS示例
xSemaphoreTake(spi_mutex, portMAX_DELAY); // 获取锁
spi_send_data(data); // 操作SPI
xSemaphoreGive(spi_mutex); // 释放锁
任务同步:
生产者任务生成数据后通过信号量通知消费者,实现无锁队列同步。
// 生产者
produce_data(buffer);
sem_post(&data_ready); // 增加信号量
// 消费者
sem_wait(&data_ready); // 等待信号量
consume_data(buffer);
4. 风险规避策略
死锁预防:
顺序加锁:所有线程按固定顺序获取多个锁(如先A后B)。
超时机制:设定锁等待时间上限(如pthread_mutex_timedlock)。
资源泄漏监控:
使用静态分析工具(如Coverity)检测未释放的锁或信号量。
总结
信号量与互斥锁通过不同策略解决资源竞争:
互斥锁:通过强制串行化实现独占访问,适用于单资源严格互斥场景;
信号量:通过计数控制管理资源池,适用于多实例资源共享或任务同步。
开发中需遵循**“最小化临界区”**原则,缩短锁持有时间,避免优先级反转与死锁。在实时系统中,可结合优先级天花板协议(Priority Ceiling)增强确定性。未来趋势将倾向于无锁数据结构(如RCU)与硬件原子操作(如ARM的LDREX/STREX指令),进一步提升并发性能。