C++多线程:condition_variable

条件变量

类似于pthread库中的pthread_cond_*()提供的功能,C++ 11标准提供了两种表示条件变量的类,分别是condition_variablecondition_variable_any,定义在头文件<condition_variable>

std::condition_variable

std::condition_variable对象调用wait()时,会阻塞当前线程,直到该std::condition_variable对象被另一线程notify_*()唤醒

构造函数

  • 默认构造
    condition_variable()
  • 拷贝构造
    condition_variable(const condition_variable&)=delete
    被禁用,不可拷贝

如下使用条件变量阻塞当前线程

std::mutex mtx;
std::condition_variable cv;

void print(int id) {
    std::unique_lock<std::mutex> lck(mtx);
    cv.wait(lck);
    // ...
}

成员函数

  1. std::condition_variable::wait()重载
  • unconditional
    void wait(unique_lock<mutex>&);
    
    在调用wait()前,此时线程应该是加锁状态,因为wait()在阻塞线程时,会自动调用lck.unlock()释放锁,使得其他被阻塞在锁竞争上的线程得以执行
    当前线程被另一线程notify_*()唤醒时,则会自动调用lck.lock()加锁,使得lck状态和wait()被调用时相同
  • predicate
    template<class Predition>
    void wait(unique_lock<mutex>&, Predition pred);
    
    仅在predfalse时,wait()才会阻塞当前线程
    当前线程被另一线程notify_*()唤醒时,仅在predtrue时才会解除阻塞
    类似于
    while(!pred){
      wait(lck);
    }
    
  1. std::condition_variable::wait_for()重载
  • unconditional
    template<class Rep, class Period>
    cv_status wait_for(unique_lock<std::mutex>&, const chrono::duration<Rep,Period>&)
    
    在当前线程被notify_*()唤醒前,或在指定时间段内,当前线程会处于阻塞状态
  • predicate
    template<class Rep, class Period, class Predicate>
    cv_status wait_for(unique_lock<std::mutex>&, const chrono::duration<Rep,Period>& rel_time, Predicate pred)
    
    仅在predfalse时,wait()才会阻塞当前线程
    当前线程被另一线程notify_*()唤醒时,仅在predtrue时才会解除阻塞
    类似于
    return wait_until(lck, chrono::steady_clock::now()+rel_time, std::move(pred));
    
  1. std::condition_variable::wait_until()重载
  • unconditional
    template<class Clock,class Duration>
    cv_status wait_until(unique_lock<mutex>&,const chrono::time_point<Clock,Duration>&)
    
    在当前线程被notify_*()唤醒前,或在指定时间点前,当前线程会处于阻塞状态
  • predicate
    template<class Clock, class Duration, class Predicate>
    cv_status wait_until(unique_lock<mutex>&, const chrono::time_point<Clock,Duration>& abs_time, Predicate pred)
    
    仅在predfalse时,wait()才会阻塞当前线程
    当前线程被另一线程notify_*()唤醒时,仅在predtrue时才会解除阻塞
    类似于
    while(!pred){
      if(wait_until(lck,abs_time)==cv_status::timeout){
          return pred;
      }
    }
    return true;
    
  1. std::condition_variable::notify_one()
    唤醒某个等待wait线程,若有多个等待线程,则唤醒的线程是不确定的

  2. std::condition_variable::notify_all()
    唤醒所有等待wait线程

std::condition_variable_any

std::condition_variable不同的是,std::condition_variable_anywait()可以接收任何lockable参数,如递归互斥锁、定时互斥锁等
std::condition_variablewait()仅可以接收std::unique_lock<std::mutex>类型

std::cv_status枚举类型

  • cv_status::no_timeout
    wait_for()wait_until()未超时,即在规定时间段内线程收到notify_*()通知
  • cv_status::timeout
    wait_for()wait_until()超时

辅助函数

std::notify_all_at_thread_exit()

函数原型

void std::notify_all_at_thread_exit(condition_variable& cond,unique_lock<std::mutex>);

当调用该函数的线程退出时,所有在cond条件变量上等待的线程都会收到通知

示例如下:

#include <iostream>
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable_any

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

void print_id(int id) {
  std::unique_lock<std::mutex> lck(mtx);
  while(!ready){
    cv.wait(lck);
  }
  std::cout << "thread id:" << id << std::endl;
}

void go() {
  std::unique_lock<std::mutex> lck(mtx);
  std::notify_all_at_thread_exit(cv, std::move(lck));
  ready= true;
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i){
        threads[i] = std::thread(print_id, i);
    }
    std::cout<< "10 threads ready to race..."<<std::endl;

    std::thread(go).detach();

    for (auto& th : threads) {
        th.join();
    }
    return 0;
}

通知丢失

虚假唤醒

posted @   sgqmax  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示