• C++11 条件变量(condition_variable) 使用详解


    官网

    一、总述

    在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。

    主要成员函数如下:

    二、具体函数:

     1、wait函数:

    (1)wait(unique_lock <mutex>&lck)

    当前线程的执行会被阻塞,直到收到 notify 为止。

    (2)wait(unique_lock <mutex>&lck,Predicate pred)

    当前线程仅在pred=false时阻塞;如果pred=true时,不阻塞。

    wait()可依次拆分为三个操作:释放互斥锁等待在条件变量上再次获取互斥锁

    2、notify_one:

    notify_one():没有参数、没有返回值。

    解除阻塞当前正在等待此条件的线程之一。如果没有线程在等待,则还函数不执行任何操作。如果超过一个,不会指定具体哪一线程。

    // condition_variable::notify_one
    #include <iostream>           // std::cout
    #include <thread>             // std::thread
    #include <mutex>              // std::mutex, std::unique_lock
    #include <condition_variable> // std::condition_variable
    
    std::mutex mtx;
    std::condition_variable produce,consume;
    
    int cargo = 0;     // shared value by producers and consumers
    
    void consumer () {
      std::unique_lock<std::mutex> lck(mtx);
      while (cargo==0) consume.wait(lck);
      std::cout << cargo << '
    ';
      cargo=0;
      produce.notify_one();
    }
    
    void producer (int id) {
      std::unique_lock<std::mutex> lck(mtx);
      while (cargo!=0) produce.wait(lck);
      cargo = id;
      consume.notify_one();
    }
    
    int main ()
    {
      std::thread consumers[10],producers[10];
      // spawn 10 consumers and 10 producers:
      for (int i=0; i<10; ++i) {
        consumers[i] = std::thread(consumer);
        producers[i] = std::thread(producer,i+1);
      }
    
      // join them back:
      for (int i=0; i<10; ++i) {
        producers[i].join();
        consumers[i].join();
      }
    
      return 0;
    }

    三、分析

    条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

    (1)、一个线程因等待“条件变量的条件成立”而挂起;

    (2)、另外一个线程使“条件成立”,给出信号,从而唤醒被等待的线程。

    1、有什么用:

    当需要死循环判断某个条件成立与否时【true or false】,我们往往需要开一个线程死循环来判断,这样非常消耗CPU。使用条件变量,可以让当前线程wait,释放CPU,如果条件改变时,我们再notify退出线程,再次进行判断。

    2、其他解释

    想要修改共享变量(即“条件”)的线程必须:
    (1). 获得一个std::mutex
    (2). 当持有锁的时候,执行修改动作
    (3). 对std::condition_variable执行notify_one或notify_all(当做notify动作时,不必持有锁)

    即使共享变量是原子性的,它也必须在mutex的保护下被修改,这是为了能够将改动正确发布到正在等待的线程。

    任意要等待std::condition_variable的线程必须:
    (1). 获取std::unique_lock<std::mutex>,这个mutex正是用来保护共享变量(即“条件”)的
    (2). 执行wait, wait_for或者wait_until. 这些等待动作原子性地释放mutex,并使得线程的执行暂停
    (3). 当获得条件变量的通知,或者超时,或者一个虚假的唤醒,那么线程就会被唤醒,并且获得mutex. 然后线程应该检查条件是否成立,如果是虚假唤醒,就继续等待。

    【注: 所谓虚假唤醒,就是因为某种未知的罕见的原因,线程被从等待状态唤醒了,但其实共享变量(即条件)并未变为true。因此此时应继续等待】

    std::deque<int> q;
    std::mutex mu;
    std::condition_variable cond;
    
    void function_1() //生产者
    {
        int count = 10;
        while (count > 0) 
        {
            std::unique_lock<std::mutex> locker(mu);
            q.push_front(count);
            locker.unlock();
            cond.notify_one();  // Notify one waiting thread, if there is one.
            std::this_thread::sleep_for(std::chrono::seconds(1));
            count--;
        }
    }
    
    void function_2() //消费者
    {
        int data = 0;
        while (data != 1) 
        {
            std::unique_lock<std::mutex> locker(mu);
            while (q.empty())
                cond.wait(locker); // Unlock mu and wait to be notified
            data = q.back();
            q.pop_back();
            locker.unlock();
            std::cout << "t2 got a value from t1: " << data << std::endl;
        }
    }
    int main() 
    {
        std::thread t1(function_1);
        std::thread t2(function_2);
        t1.join();
        t2.join();
        return 0;
    }

    核心:

    ①、在消费者里判断队列是否为空后,如果不为空则wait,等待生产者发送notify信号

    ②、在生产者那里,如果生产了任务,则发送notify信号,告诉消费者可以试图退出wait,判断队列是否为空,如果有任务则调度处理任务,如果还是空则说明此次notify是错误的,可能是其他地方发出来干扰的,生产者继续wait。

    ③、流程:

    软件开启,生成消费者线程消费队列,应该是一个while循环,在循环里获取锁,再来一个while循环判断条件,如果条件不成立则wait,wait会自动释放锁;

    此时消费者已经没有锁了,在生产者线程里,获取锁,然后往里面加任务,退出作用域释放锁,然后notify告知消费者退出wait,消费者重新获取锁,然后从队列里取任务;

    整个过程,生产者加任务时生产者持有锁,消费者取任务时消费者持有锁。

    资料:

    https://www.cnblogs.com/judes/p/11132918.html

    https://www.cnblogs.com/judes/p/11230628.html

  • 相关阅读:
    HDU 5585 Numbers
    HDU 3308 LCIS
    POJ 2991 Crane
    POJ 1436 Horizontally Visible Segments
    POJ 3667 Hotel
    HaiHongOJ 1003 God Wang
    【SDOI 2008】 递归数列
    5月19日省中提高组题解
    【HDU 1588】 Gauss Fibonacci
    【POJ 3233】Matrix Power Series
  • 原文地址:https://www.cnblogs.com/xiaohaigegede/p/14008121.html
Copyright © 2020-2023  润新知