zoukankan      html  css  js  c++  java
  • C++多线程之条件变量

    C++多线程之条件变量 - condition_variable


    condition_variable是一个能够堵塞调用线程直到被通知恢复的对象。

    当调用condition_variable的某个等待函数时,它使用unique_lock来锁定线程。该线程会保持堵塞状态,直到被另一个线程通过调用同一个condition_variable对象的通知函数来唤醒为止。

    1.wait(...)

    void wait(unique_lock<mutex>& lck);
    
    template<class Predicate>
    void wait(unique_lock<mutex>& lck, Predicate pred())
    

    调用wait函数后,当前线程的执行被堵塞,直到得到通知。
    再阻塞线程时,该函数为自动调用lck.unlock(),从而允许其它被锁定的线程能继续执行。
    当收到其它线程的通知时,该函数将取消阻塞并调用lck.lock(),使lck处于调用该函数时相同的状态,然后函数返回(注意,最后的互斥锁可能会在返回之前再次阻塞线程)。
    通常通过另一个线程调用notify_one或notify_all来通知该函数唤醒,但是某些实现可能会产生虚假的调用唤醒,而不会调用这些函数中的任何一个,因此使用此功能时应该确保满足其恢复条件,我们应该保证总是在循环中使用普通的wait函数。例如:while(...) cv.wait()

    如果指定了pred,则该函数仅再pred返回false时菜阻塞,并且通知只能在线程变为true时才能取消阻塞线程,这对检查虚假唤醒调用特别有用。等同于:while(!pred()) wait(lck)

    另外需要特别注意的是在销毁一个condition_variable时必须通知所有处于等待状态的线程,否则它们可能会一直等待下去。
    实例:

    #include <iostream>
    #include <thread>
    #include <condition_variable>
    #include <mutex>
    
    std::mutex gMutex;
    std::condition_variable gCv;
    
    int gCargo;
    
    bool sufficient()
    {
        return 0 != gCargo;
    }
    
    void consume(int n)
    {
        for(int i = 0; i < n; ++i)
        {
            std::unique_lock<std::mutex> lck(gMutex);
            gCv.wait(lck, sufficient);
            std::cout << "cargo: " << gCargo << std::endl;
            gCargo = 0;
        }
        
    }
    
    int main()
    {
        std::thread consume_tread(consume, 10);
        for(int i = 0; i < 10; ++i)
        {
            while(sufficient())
            {
                std::this_thread::yield();
            }
            std::unique_lock<std::mutex> lck(gMutex);
            gCargo = i + 1;
            gCv.notify_one();
        }
        consume_tread.join();
        return 0;
    }
    

    2.wait_until(...)

    template <class Clock, class Duration>
    cv_status wait_until (unique_lock<mutex>& lck,
                          const chrono::time_point<Clock,Duration>& abs_time);
    

    当前线程的执行被阻塞,直到被通知或直到abs_time为止(以先发生者为准)。

    在阻塞线程时,该函数会自动调用lck.unlock(),从而允许其他锁定的线程继续执行。

    收到通知或到达abs_time后,该函数将取消阻塞并调用lck.lock(),使lck处于与调用该函数相同的状态。然后函数返回(注意,最后的互斥锁可能会在返回之前再次阻塞线程)。

    template <class Clock, class Duration, class Predicate>
    bool wait_until (unique_lock<mutex>& lck,
                     const chrono::time_point<Clock,Duration>& abs_time,
                     Predicate pred);
    

    如果指定了pred,等同于:

    while (!pred())
      if ( wait_until(lck,abs_time) == cv_status::timeout)
        return pred();
    

    3.wait_for(...)

    template<class Rep, class Period>
    cv_status wait_for(unique_lock<mutex>& lck,
                       const chrono::duration<Rep, Period>& rel_time);
    

    等同于:

    cv.wait_until(lck, steady_clock::now + rel_time)
    

    示例:

    // condition_variable::wait_for example
    #include <iostream>           // std::cout
    #include <thread>             // std::thread
    #include <chrono>             // std::chrono::seconds
    #include <mutex>              // std::mutex, std::unique_lock
    #include <condition_variable> // std::condition_variable, std::cv_status
    
    std::condition_variable cv;
    
    int value;
    
    void read_value() {
      std::cin >> value;
      cv.notify_one();
    }
    
    int main ()
    {
      std::cout << "Please, enter an integer (I'll be printing dots): 
    ";
      std::thread th (read_value);
    
      std::mutex mtx;
      std::unique_lock<std::mutex> lck(mtx);
      while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
        std::cout << '.' << std::endl;
      }
      std::cout << "You entered: " << value << '
    ';
    
      th.join();
    
      return 0;
    }
    
    template<class Rep, class Period, class Predicate>
    bool wait_for(unique_lock<mutex>* lck, 
                 const chrono::duration<Rep, Period>& rel_time,
                 Predicate pred);
    

    等同于:

    cv.wait_until(lck, steady_clock::now() + rel_time, move(pred))
    

    4.cv_status

    enum class cv_status { no_timeout, timeout };
    

    5.notify_one

    void notify_one() noexcept;
    

    从正在等待该条件变量的所有线程中解除一个线程的阻塞。如果没有线程在等待,则该函数不执行任何操作。如果有多个线程在等待它也不会指定解除哪个线程的阻塞状态。

    6.notify_all

    void notify_all() noexcept;
    

    解除所有正在等待该条件变量线程的阻塞状态。如果没有线程在等待,则该函数不执行任何操作。

    参考链接链接

  • 相关阅读:
    struts2ModelDriven模型驱动
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    struts2Token Interceptor(处理表单重复提交)
    strutsOGNL标签
    嵌入式关系型SQLite数据库
    SQLite数据库的增删改查
    Pull解析器解析XML文件和生成XML文件
    proguard.cfg 系统找不到指定的文件
    struts2ONGL原理和表达式
    写代码的三重境界 Hanson
  • 原文地址:https://www.cnblogs.com/chengjundu/p/12420608.html
Copyright © 2011-2022 走看看