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

条件变量和信号量的区别

条件变量 vs 信号量

1. 条件变量 (std::condition_variable)

📌 作用

  • 适用于线程间的等待和通知机制,即一个线程等待某个条件成立,而另一个线程在条件满足时通知它。
  • 需要与**互斥锁(std::mutex)**配合使用,以保证对共享资源的访问是安全的。

📌 工作方式

  1. 线程获取锁 (std::unique_lock<std::mutex>)
  2. 使用 wait() 进入等待状态自动释放锁
  3. 另一个线程修改条件后,调用 notify_one()notify_all() 进行通知
  4. wait() 被唤醒后,重新获取锁,并检查条件是否满足,若满足则继续执行

📌 代码示例

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, [] { return ready; });  // 释放锁并等待
    std::cout << "Worker is running!\n";
}

int main() {
    std::thread t(worker);

    std::this_thread::sleep_for(std::chrono::seconds(1));

    {  // 作用域保证锁的正确释放
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();  // 唤醒一个线程

    t.join();
    return 0;
}

📌 特性

等待时释放锁,避免死锁
适用于复杂条件的同步(例如等待任务队列不为空)
只能用于线程间的同步,不能跨进程
等待时可能会发生虚假唤醒(Spurious Wakeup),所以通常需要在 wait() 里重复检查条件


2. 信号量 (std::counting_semaphore / std::binary_semaphore)

📌 作用

  • 用于限制资源的访问数量,如控制线程池的并发数,或者控制对某个资源的访问次数。
  • 不依赖互斥锁 (std::mutex),可以在多个线程或进程之间同步

📌 工作方式

  1. 信号量有一个初始计数值(代表可用资源数量)
  2. acquire()(P 操作):等待资源可用,然后减少计数
  3. release()(V 操作):增加计数,通知其他等待线程

📌 代码示例

#include <iostream>
#include <thread>
#include <semaphore>  // C++20 引入的信号量

std::counting_semaphore<2> sem(2);  // 允许最多2个线程访问

void worker(int id) {
    sem.acquire();  // 请求资源
    std::cout << "Worker " << id << " is running\n";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    sem.release();  // 释放资源
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    std::thread t3(worker, 3);

    t1.join();
    t2.join();
    t3.join();
    return 0;
}

📌 特性

不会发生虚假唤醒
适用于资源管理(如限制并发数)
不依赖互斥锁,可用于跨进程同步
只能用于计数,不适用于复杂条件等待(如“任务队列不为空”这种条件)


3. 条件变量 vs 信号量

对比项条件变量 (std::condition_variable)信号量 (std::counting_semaphore)
适用场景线程间的等待和通知(如任务队列)资源访问控制(如线程池、流量控制)
依赖互斥锁需要 std::mutex不需要 std::mutex
等待时是否释放锁,等待时释放锁不适用(不依赖锁)
是否可能虚假唤醒(必须在 wait() 里检查条件)不会发生
是否可用于跨进程是(POSIX 信号量)
适用于复杂条件适用(如“任务队列不为空”)不适用(只能计数)

4. 什么时候用条件变量,什么时候用信号量?

std::condition_variable 的情况

  • 需要线程间等待某个特定条件(如等待队列不为空)
  • 需要多个线程协调任务执行
  • 需要等待某些任务完成(如生产者-消费者模型)

std::counting_semaphore 的情况

  • 需要限制资源的并发访问数(如线程池)
  • 需要控制某个操作的最大并发数(如数据库连接池)
  • 需要跨进程同步(信号量可以在多个进程之间共享)

5. 总结

  • 条件变量 适用于等待某个特定条件,适合任务队列、生产者-消费者模式。
  • 信号量 适用于控制资源访问数量,适合线程池、流量控制、资源管理等场景。

如果你在写线程池,通常会用信号量来控制并发数,而在写任务队列时,通常会用条件变量来等待任务到来。


http://www.kler.cn/a/549035.html

相关文章:

  • Cursor笔记
  • 七、Java常用API(2)
  • 使用 DeepSeek 生成商城流程图
  • Flutter 打包报错:Execution failed for task ‘:flutter_plugin_android_lifecycle的解决办法
  • AI、Java、C++、C#等技术学习资源链接汇总
  • HarmonyOS NEXT网络状态监听HTTP和RCP请求网络
  • java集合一张图理解
  • 蓝桥杯 Java B 组之简单动态规划(爬楼梯、斐波那契数列)
  • 大数据SQL调优专题——Hive执行原理
  • 设计模式与面向对象的 11 个致命陷阱
  • Java语言介绍
  • 四、敏捷方法论:敏捷开发中的关键实践
  • Python爬虫入门:从数据海洋中获取宝藏
  • Spring Boot02(数据库、Redis)---java八股
  • IDEA通过Contince接入Deepseek
  • 深度学习训练camp-第R3周:RNN-心脏病预测
  • 腿足机器人之六- 前向运动学
  • OpenCV的主要模块
  • golang字符串拼接实现方式和比较
  • 跟着李沐老师学习深度学习(十)