当前位置: 首页 > news >正文

锁(Mutex)、信号量(Semaphore)与条件量(Condition Variable)


一、同步机制的核心意义

在多线程/多进程编程中,当多个执行流共享资源(如变量、内存、文件)时,可能因操作顺序不确定导致数据竞争(Data Race)。同步机制的作用是:

  1. 保证原子性:确保关键代码段(Critical Section)的独占访问。
  2. 协调执行顺序:控制线程/进程的执行顺序(如生产者-消费者模型)。

二、锁(Mutex)

1. 基本概念
  • 互斥锁(Mutex):最简单的同步工具,确保同一时间只有一个线程访问共享资源。
  • 特性:锁被占用时,其他尝试获取锁的线程会被阻塞,直到锁被释放。
2. 使用步骤
  1. 初始化锁
  2. 进入临界区前加锁
  3. 退出临界区后解锁
  4. 销毁锁
3. 示例:多线程计数器
#include <pthread.h>
#include <stdio.h>int counter = 0;
pthread_mutex_t lock; // 定义互斥锁void* increment(void* arg) {for (int i = 0; i < 100000; i++) {pthread_mutex_lock(&lock); // 加锁counter++;pthread_mutex_unlock(&lock); // 解锁}return NULL;
}int main() {pthread_t t1, t2;pthread_mutex_init(&lock, NULL); // 初始化锁pthread_create(&t1, NULL, increment, NULL);pthread_create(&t2, NULL, increment, NULL);pthread_join(t1, NULL);pthread_join(t2, NULL);printf("Final counter: %d\n", counter); // 正确输出 200000pthread_mutex_destroy(&lock); // 销毁锁return 0;
}

关键点:若不加锁,counter++(非原子操作)会导致结果错误。


三、信号量(Semaphore)

1. 基本概念
  • 信号量:一种更通用的同步工具,可以控制多个线程对资源的访问。
  • 类型
    • 二进制信号量:取值0或1,功能类似锁。
    • 计数信号量:允许指定数量的线程同时访问资源。
2. 使用场景
  • 限制资源并发访问数(如数据库连接池)。
  • 生产者-消费者模型(缓冲区大小控制)。
3. 示例:生产者-消费者模型
#include <semaphore.h>
#include <pthread.h>
#define BUFFER_SIZE 5int buffer[BUFFER_SIZE];
sem_t empty, full; // 定义信号量
int in = 0, out = 0;void* producer(void* arg) {for (int i = 0; i < 10; i++) {sem_wait(&empty); // 等待空位(empty-1)buffer[in] = i;in = (in + 1) % BUFFER_SIZE;sem_post(&full); // 通知有数据(full+1)}return NULL;
}void* consumer(void* arg) {for (int i = 0; i < 10; i++) {sem_wait(&full); // 等待数据(full-1)printf("Consumed: %d\n", buffer[out]);out = (out + 1) % BUFFER_SIZE;sem_post(&empty); // 释放空位(empty+1)}return NULL;
}int main() {sem_init(&empty, 0, BUFFER_SIZE); // 初始空位数量=5sem_init(&full, 0, 0);            // 初始数据数量=0pthread_t p, c;pthread_create(&p, NULL, producer, NULL);pthread_create(&c, NULL, consumer, NULL);pthread_join(p, NULL);pthread_join(c, NULL);sem_destroy(&empty);sem_destroy(&full);return 0;
}

关键点:通过 emptyfull 信号量协调生产者和消费者的执行顺序。


四、条件变量(Condition Variable)

1. 基本概念
  • 条件变量:允许线程在某个条件不满足时主动阻塞,并在条件满足时被唤醒。
  • 必须与互斥锁配合使用:确保检查和修改条件的原子性。
2. 使用场景
  • 等待特定条件(如任务队列非空)。
  • 避免忙等待(Busy Waiting),节省CPU资源。
3. 示例:任务队列调度
#include <pthread.h>
#include <stdbool.h>pthread_mutex_t lock;
pthread_cond_t cond;   // 条件变量
bool task_available = false;void* worker(void* arg) {pthread_mutex_lock(&lock);while (!task_available) {pthread_cond_wait(&cond, &lock); // 阻塞并释放锁,被唤醒后自动重新加锁}printf("Processing task...\n");task_available = false;pthread_mutex_unlock(&lock);return NULL;
}void* scheduler(void* arg) {pthread_mutex_lock(&lock);task_available = true;pthread_cond_signal(&cond); // 唤醒等待的线程pthread_mutex_unlock(&lock);return NULL;
}int main() {pthread_t t_worker, t_scheduler;pthread_mutex_init(&lock, NULL);pthread_cond_init(&cond, NULL);pthread_create(&t_worker, NULL, worker, NULL);sleep(1); // 确保worker先进入等待pthread_create(&t_scheduler, NULL, scheduler, NULL);pthread_join(t_worker, NULL);pthread_join(t_scheduler, NULL);pthread_mutex_destroy(&lock);pthread_cond_destroy(&cond);return 0;
}

关键点

  • pthread_cond_wait原子性地释放锁并阻塞线程,被唤醒后重新获取锁。
  • 必须使用 while 检查条件(避免虚假唤醒)。

五、三者的对比与选择

机制用途特点
保护临界区,确保独占访问简单、轻量,仅支持互斥
信号量控制资源访问数量或协调执行顺序灵活,支持计数和复杂同步逻辑
条件变量等待特定条件成立需与锁配合,避免忙等待

选择原则

  • 简单互斥 → 锁。
  • 控制资源数量 → 信号量。
  • 等待条件成立 → 条件变量。

六、深入:常见问题与陷阱

  1. 死锁

    • 场景:多个锁未按顺序获取。
    • 解决:统一加锁顺序,或使用超时机制(如 pthread_mutex_trylock)。
  2. 优先级反转

    • 场景:低优先级线程持有高优先级线程需要的锁。
    • 解决:优先级继承(如 pthread_mutexattr_setprotocol)。
  3. 虚假唤醒

    • 场景:pthread_cond_wait 可能无故返回。
    • 解决:始终在 while 循环中检查条件。

通过合理使用锁、信号量和条件变量,可以构建高效且安全的并发程序。实际开发中需严格测试同步逻辑。

http://www.xdnf.cn/news/24175.html

相关文章:

  • 网络编程2
  • 第八周作业
  • alertManager部署安装、告警规则配置详解及告警消息推送
  • 工厂方法模式详解及在自动驾驶场景代码示例(c++代码实现)
  • Linux根据 PID 进行性能分析
  • 三格电子——PROFIBUS DP设备长距离传输和干扰问题解决办法
  • ffprobe 输出 HEVC 码流 Level:标准的 “错位” 与分析的 “归位”
  • javaweb-servlet-继承关系以及service方法、servlet生命周期
  • LabelImg打标工具的下载和使用——YOLO格式篇
  • open CasCade下载
  • RVOS的任务调度优化
  • OJ笔试强训_1至24天
  • `peft`(Parameter-Efficient Fine-Tuning:高效微调)是什么
  • 接口测试的原则、用例与流程
  • Git学习之路(Updating)
  • 《软件设计师》复习笔记(11.3)——需求获取、分析、定义、验证、管理
  • 欧拉系统升级openssh 9.7p1
  • 【AI】实现中文文章摘要的AI模型
  • 【失败】Gnome将默认终端设置为 Kitty
  • 如何在Linux系统中部署C++ Web应用
  • Sa-Token使用指南
  • 1 Celery 简介
  • cpolar 内网穿透 实现公网可以访问本机
  • top100 (6-10)
  • 字符串循环拼接,不能用 + 连接, 需要用 StringBuilder 代替
  • 全球唯一电解方式除湿器 / 加湿器 RS1 ROSAHL 微型 易安装
  • Logisim数字逻辑实训——寄存器设计与应用
  • 稳态模型下的异步电机调速【运动控制系统】
  • 《软件设计师》复习笔记(13)——结构化开发方法
  • 2021-11-09 C++倍数11各位和为13