精华内容
下载资源
问答
  • C++并发编程之线程同步 std::condition_variable用法总结
    2021-09-23 22:06:28

    1、std::condition_variable介绍

    在C11多线程编程中,仅仅通过std::mutex锁住资源,控制不同线程操作资源的顺序,这是不够的。有的时候当前线程需要的数据需要其他线程做处理再通知当前线程继续运行,这时候就需要不同线程之间的控制了。条件变量std::condition_variable就是一个方式。

    std::condition_variable 是条件变量,其作用是配合std::mutex。当前线程获得互斥量std::mutex的时候,std::condition_variable的对象的成员函数wait时,
    当前线程阻塞在调用waite的地方。什么时候能继续往下执行呢,需要std::condition_variable对象被调用notification的时候,当前线程就能继续往下执行了。简单来说就是,通过std::mutex获得当前线程需要的资源并锁住,通过std::condition_variable控制另外一个线程的的运行。本文介绍std::condition_variable的成员函数及其区别和具体用法。
    首先看下std::condition_variable的定义,如下所示:

      /// condition_variable
      class condition_variable
      {
        typedef chrono::system_clock	__clock_t;
        typedef __gthread_cond_t		__native_type;
        __native_type			_M_cond;
      public:
        typedef __native_type* 		native_handle_type;
        condition_variable();
        ~condition_variable();
        condition_variable(const condition_variable&) = delete;
        condition_variable& operator=(const condition_variable&) = delete;
        void
        notify_one();
        void
        notify_all();
        void
        wait(unique_lock<mutex>& __lock);
        template<typename _Predicate>
          void
          wait(unique_lock<mutex>& __lock, _Predicate __p)
          {
    	  while (!__p())
    	  wait(__lock);
          }
    
        template<typename _Duration>
          bool
          wait_until(unique_lock<mutex>& __lock,
    		 const chrono::time_point<__clock_t, _Duration>& __atime)
          { return __wait_until_impl(__lock, __atime); }
          
        template<typename _Clock, typename _Duration>
          bool
          wait_until(unique_lock<mutex>& __lock,
    		 const chrono::time_point<_Clock, _Duration>& __atime)
          {
          
    		// DR 887 - Sync unknown clock to known clock.
    		typename _Clock::time_point __c_entry = _Clock::now();
    		__clock_t::time_point __s_entry = __clock_t::now();
    		chrono::nanoseconds __delta = __atime - __c_entry;
    		__clock_t::time_point __s_atime = __s_entry + __delta;
    		return __wait_until_impl(__lock, __s_atime);
          }
    
        template<typename _Clock, typename _Duration, typename _Predicate>
          bool
    	  wait_until(unique_lock<mutex>& __lock,
    			 const chrono::time_point<_Clock, _Duration>& __atime,
    			 _Predicate __p)
    	      {
    		while (!__p())
    		  if (!wait_until(__lock, __atime))
    		    return __p();
    	
    		return true;
          }
    
        template<typename _Rep, typename _Period>
          bool
          wait_for(unique_lock<mutex>& __lock,
    	       const chrono::duration<_Rep, _Period>& __rtime)
          { return wait_until(__lock, __clock_t::now() + __rtime); }
    
          template<typename _Rep, typename _Period, typename _Predicate>
          bool
          wait_for(unique_lock<mutex>& __lock,
    	       const chrono::duration<_Rep, _Period>& __rtime,
    	       _Predicate __p)
          { return wait_until(__lock, __clock_t::now() + __rtime, std::move(__p)); }
    
        native_handle_type
        native_handle()
        { return &_M_cond; }
    
      private:
        template<typename _Clock, typename _Duration>
          bool
          __wait_until_impl(unique_lock<mutex>& __lock,
    			const chrono::time_point<_Clock, _Duration>& __atime)
          {
    	chrono::time_point<__clock_t, chrono::seconds> __s =
    	  chrono::time_point_cast<chrono::seconds>(__atime);
    
    	chrono::nanoseconds __ns =
    	  chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
    
    	__gthread_time_t __ts =
    	  {
    	    static_cast<std::time_t>(__s.time_since_epoch().count()),
    	    static_cast<long>(__ns.count())
    	  };
    
    	__gthread_cond_timedwait(&_M_cond, __lock.mutex()->native_handle(),
    				 &__ts);
    
    	return _Clock::now() < __atime;
          }
      };
    

    其中比较重要的成员函数如下:

    void notify_one();
    void notify_all();
    
    template<typename _Duration>
    bool wait_until(unique_lock<mutex>& __lock,const chrono::time_point<__clock_t, _Duration>& __atime);
    
    template<typename _Clock, typename _Duration>
    bool wait_until(unique_lock<mutex>& __lock,const chrono::time_point<_Clock, _Duration>& __atime);
    
    template<typename _Clock, typename _Duration, typename _Predicate>
    bool wait_until(unique_lock<mutex>& __lock, const chrono::time_point<_Clock, _Duration>& __atime, _Predicate __p)
    
    template<typename _Rep, typename _Period>
    bool wait_for(unique_lock<mutex>& __lock,const chrono::duration<_Rep, _Period>& __rtime)
    
    template<typename _Rep, typename _Period, typename _Predicate>
    bool wait_for(unique_lock<mutex>& __lock,const chrono::duration<_Rep, _Period>& __rtime,_Predicate __p)
    

    2、std::condition_variable重要成员函数介绍

    std::condition_variable构造函数

    condition_variable();
    ~condition_variable();
    condition_variable(const condition_variable&) = delete;//不允许拷贝构造
    condition_variable& operator=(const condition_variable&) = delete;//不允许move拷贝
    

    std::condition_variable::notify_one() 函数
    当前线程操作std::condition_variable对象,执行notify_one成员函数时,其作用是唤醒此对象std::condition_variable的wait的线程。如果说存在多个线程都在此等待,则唤醒具体某个线程未知。

    std::condition_variable::notify_all() 函数
    当前线程操作std::condition_variable对象,执行notify_all成员函数时,其作用是唤醒此对象std::condition_variable的wait的所有线程。梭哈

    std::condition_variable::wait_for() 函数
    其定义的模板接口如下:

    template<typename _Rep, typename _Period>
    bool wait_for(unique_lock<mutex>& __lock,const chrono::duration<_Rep, _Period>& __rtime)
    template<typename _Rep, typename _Period, typename _Predicate>
    bool wait_for(unique_lock<mutex>& __lock,const chrono::duration<_Rep, _Period>& __rtime,_Predicate __p)
    

    上面有两个接口函数,一个是waite_for指定时间段,在当前线程收到通知或者指定时间前,线程都会在阻塞状态。一个是增加了Predicate的标志位,只有接收到通知后且__p为true的时候,才能接触阻塞,也就是说一个是接收到消息就解除阻塞,一个还需要一个标志位控制是否接触阻塞。

    std::condition_variable::wait_until()函数

    template<typename _Duration>
    bool wait_until(unique_lock<mutex>& __lock,const chrono::time_point<__clock_t, _Duration>& __atime);
    template<typename _Clock, typename _Duration>
    bool wait_until(unique_lock<mutex>& __lock,const chrono::time_point<_Clock, _Duration>& __atime);
    template<typename _Clock, typename _Duration, typename _Predicate>
    bool wait_until(unique_lock<mutex>& __lock, const chrono::time_point<_Clock, _Duration>& __atime, _Predicate __p)
    

    其有三个接口函数,具体的作用和waite_for差不多,就是wait_until指定一个时间点,当前的线程在时间点前,或者超时之前,线程都会阻塞。一旦超时了或者接收到了其他控制线程的通知,则当前阻塞接触,继续往下执行。

    std::condition_variable_any 函数

    3、std::condition_variable使用示例

    /*************************************************************************
    	> File Name: thread_condition_variable.cpp
    	> Author: 小和尚敲木鱼
    	> Mail:  
    	> Created Time: Mon 20 Sep 2021 08:17:49 AM PDT
     ************************************************************************/
    
    #include <iostream>             // std::cout
    #include <thread>               // std::thread
    #include <mutex>                // std::mutex, std::unique_lock
    #include <condition_variable>   // std::condition_variable
    using namespace std;
    
    /*****************************文件说明***********************************
     ***********************************************************************/
    std::mutex g_Mtx;
    std::condition_variable g_ConditionVar; 
    bool g_Running = true;
    static int run_count = 0;
    void print_()//打印计数
    {
    	std::cout << "[enter]"<< __func__ << std::endl;
    	while (g_Running) 
    	{
    		{	
    			//std::cout << "get lck" << std::endl;
    			std::unique_lock <std::mutex> lck(g_Mtx);
    			g_ConditionVar.wait(lck);
    			std::cout << "print thread run count " << run_count << std::endl;
    			run_count++;
    		}
    		std::this_thread::sleep_for(std::chrono::milliseconds(20));
    	}
    	std::cout << "[exit]"<< __func__ << std::endl;
    }
    
    void mian_control_()//控制唤醒子线程10次,打印计数
    {
    	std::cout << "[enter]"<< __func__ << std::endl;
    	for (int i = 0;i < 10; i ++) {
    		{
    			std::unique_lock <std::mutex> lck(g_Mtx);
    			g_ConditionVar.notify_one();
    		}
    		std::this_thread::sleep_for(std::chrono::milliseconds(100));
    	}
    	g_Running = false;
    	std::unique_lock <std::mutex> lck(g_Mtx);
    	g_ConditionVar.notify_one();
    	std::cout << "[exit]"<< __func__ << std::endl;
    }
    int main()
    {
    	std::cout << "condition_variable test" << std::endl;
    	g_Running = true;
    	std::thread thread1(print_);
    	mian_control_();
    
    	if (thread1.joinable())
    		thread1.join();
    	
    	return 0;
    }
    //OUT
    //condition_variable test
    //[enter]mian_control_
    //[enter]print_
    //print thread run count 0
    //print thread run count 1
    //print thread run count 2
    //print thread run count 3
    //print thread run count 4
    //print thread run count 5
    //print thread run count 6
    //print thread run count 7
    //print thread run count 8
    //[exit]mian_control_
    //print thread run count 9
    //[exit]print_
    /**************************end of file**********************************/
    
    

    4、总结

    总的来说,std::condition_variable一般用于,一个线程控制另一个线程的运行,一般是主线程控制子线程运行。成员函数也比较简单,就notify通知wait_的线程运行就可以了。具体的就是各种等待的方式不一样罢了。

    更多相关内容
  • 详细介绍了线程同步条件变量condition_variable的使用和它的源码,涉及到unique_lock, mutex, lock_guard, 虚假唤醒和惊群效应。
  • 原文链接:并发之(条件变量:condition_variable、condition_variable_any) 一、Condition Variable(条件变量)的意图 在前文的文章中,我们有一个演示案例,让某线程等待另一线程,其使用的办法是使用ready ...

    原文链接:并发之(条件变量:condition_variable、condition_variable_any)

    一、Condition Variable(条件变量)的意图

    • 在前文的文章中,我们有一个演示案例,让某线程等待另一线程,其使用的办法是使用ready flag的方法。代码如下:
    bool readyFlag;
    std::mutex readyFlagMutex;
     
    void thread1()
    {
        //做一些thread2需要的准备工作
        //...
        std::lock_guard<std::mutex> lg(readyFlagMutex);
        readyFlag = true;
    }
     
    void thread2()
    {
        //等待readyFlag变为true
        {
            std::unique_lock<std::mutex> ul(readyFlagMutex);
            //如果readyFlag仍未false,说明thread1还没有锁定,那么持续等待
            while (!readyFlag)
            {
                ul.unlock();
                std::this_thread::yield();
                std:this_thread::sleep_for(std::chrono::milliseconds(100));
                ul.lock();
            }
        }//释放lock
     
        //在thread1锁定之后,做相应的事情
    }
    • 但是上面的代码效率很低,原因有:

    • 标准库提供了条件变量它是个变量,借助它,一个线程可以唤醒一或多个其他等待中的线程
    • 想要使用条件变量,需要配合mutex、unique_lock使用
      • 因为条件变量也是一种变量,可能会有多个线程对其访问,因此在使用条件变量前,我们需要锁定一个mutex,然后这样才可以做好并发操作
      • 在notify()端:当条件满足时,我们可以调用notify_one()或notify_all()通知wait()等待端条件满足
      • 在wait()端:因为有多个线程需要对条件变量进行访问,因此我们先锁定mutex,此处用的是unique_lock<>锁住muex。然后进行一系列的操作,最终调用wait()等待在条件变量上,等待的时候其他线程可能还需要使用mutex,因此在wait()的内部会调用unique_lock的unlock()函数,在等待的过程中将mutex进行解锁,这样wait()的时候才不会因为mutex永久阻塞而造成其他线程无法继续向前执行
        • 但是不能使用lock_guard,因为等待中的函数有可能锁定或解除mutex,但是lock_guard不提供lock()或unlock()函数,unique_lock()提供lock()或unlock()函数
    • “假醒”的注意事项:
      • “假醒”就是某个条件变量的wait()动作有可能在该条件变量尚未被notified时便返回
      • 以下引自Anthony Williams的[Williams:CondVar]:“假醒无法被测定,以使用者的观点来看它们实质上是随机的。然而它们通常发生于thread library无法可靠确定某个waiting thread不遗漏任何notification时。由于遗漏notification便代表条件变量无用,thread library宁愿在线程的wait之中唤醒它而不愿承受风险”
      • 因此,发生wakeup不一定意味着线程所需要的条件已经掌握了。更确切地说,在wakeup之后你仍然需要代码去验证“条件实际已达成”。因此(例如)我们必须检查数据是否真正备妥,或是我们仍需要诸如ready flag之类的东西。为了假设和查询其他端供应的数据或ready flag,可使用同一个mutex

    二、演示案例

    #include <iostream>
    #include <thread>
    #include <future>
    #include <chrono>
    #include <condition_variable>
    using namespace std;
     
    bool readyFlag;
    std::mutex readyMutex;
    std::condition_variable readyCondVar;
     
    void thread1()
    {
        std::cout << "thread1 starting..." << std::endl;
        std::cout << "cin:" << std::endl;
        std::cin.get();
     
        {
            //锁住条件变量,然后将readyFlag变为true
            std::lock_guard<std::mutex> lg(readyMutex);
            readyFlag = true;
        }//作用域结束之后,条件变量自动释放
     
        //通知其他阻塞在readyCondVar条件变量上的代码
        readyCondVar.notify_one();
        std::cout << "thread1 done" << std::endl;
    }
     
    void thread2()
    {
        std::cout << "thread2 starting..." << std::endl;
     
        {
            std::unique_lock<std::mutex> ul(readyMutex);
            //等待在readyCondVar条件变量上(参数2见下面介绍)
            readyCondVar.wait(ul, [] {return readyFlag; });
        }
     
        std::cout << "thread2 done" << std::endl;
    }
     
    int main()
    {
        auto f1 = std::async(std::launch::async, thread1);
        auto f2 = std::async(std::launch::async, thread2);
     
        this_thread::sleep_for(std::chrono::minutes(1));
    }
    • 运行结果
    • 当我们随便输入一个字符之后,线程1先结束,然后thread1()中的条件变量通知thread2()中的wait(),使其返回,然后thread2()也跟着结束

    代码解释

     

    • 在thread1()中(通知者):
      • 我们锁住readyMutex,然后更新readyFlag,之后当作用域结束之后,readyMutex自动被释放
      • 然后通知条件变量readyCondVar
    • 在thread2()中(等待者):
      • 先是用一个unique_lock锁住mutex,然后等待wait条件变量readyCondVar
      • 注意,此处不能使用lock_guard,而必须使用unique_lock,因为wait()内部会明确地对mutex进行解锁和加锁
      • 注意此处wait()有两个参数:第一个参数为unique_lock,第二个参数是一个lambda,用来二次检测条件释放真的满足了,wait()在返回时内部会调用该lambda,然后判断readyFlag的值为true还是false,如果是true才真正的苏醒,否则继续等待
        • wait()有两个重载版本,这个代码中使用的是含有两个参数的版本;还有一个版本只有一个参数
    • 因此thread2()的wait()旁边的代码相当于下面的样子:

    三、使用条件变量实现多线程Queue

    #include <iostream>
    #include <thread>
    #include <future>
    #include <chrono>
    #include <queue>
    #include <condition_variable>
    using namespace std;
     
    std::queue<int> _queue;
    std::mutex queueMutex;
    std::condition_variable queueCondVar;
     
    void provider(int val)
    {
        for (int i = 0; i < 6; ++i)
        {
            {
                //锁定mutex,然后向_queue中加入元素
                std::lock_guard<std::mutex> lg(queueMutex);
                _queue.push(val + i);
            }
            //通知等待在条件变量上的线程
            queueCondVar.notify_one();
            //延迟一会儿
            std::this_thread::sleep_for(std::chrono::milliseconds(val));
        }
    }
     
    void consumer(int num)
    {
        while (true)
        {
            int val;
            {
                //先锁住互斥量,然后对条件变量进行操作
                std::unique_lock<std::mutex> ul(queueMutex);
                //等待条件变量通知,如果收到通知,并且_queue不为空才真正苏醒返回
                queueCondVar.wait(ul, [] {return !_queue.empty(); });
                val = _queue.front();
                _queue.pop();
            }
            std::cout << "consumer: " << num << ": " << val << std::endl;
        }
    }
     
    int main()
    {
        auto p1 = std::async(std::launch::async, provider, 100);
        auto p2 = std::async(std::launch::async, provider, 300);
        auto p3 = std::async(std::launch::async, provider, 500);
     
        auto c1 = std::async(std::launch::async, consumer, 1);
        auto c2 = std::async(std::launch::async, consumer, 2);
     
        std::this_thread::sleep_for(std::chrono::minutes(1));
    }

    四、细说条件变量

    • 头文件<condition_variable>针对条件变量提供了两个class,分别为condition_variable和condition_variable_any
    • condition_variable
      • condition_variable用来唤醒一个或多个等待在某特定条件上的线程,下面列出了condition_variable提供的操作:
      • 所有等待(wait)某个条件的线程都必须使用相同的mutex,且必须使用unique_lock绑定mutex,并且让wait()等待在unique_lock上,否则会发生不明确的行为

    • 如果无法创建条件变量,构造函数会抛出std::system_error异常并带有差错码resource_unavailable_try_again(相当于POSIX中 errno中的EAGAIN)
    • 条件变量不提供复制和赋值操作
    • wait_...()有两种重载形式:
      • 一种是没有pred参数的:当收到条件变量通知时,直接返回
      • 一种是带pred参数的:当收到条件变量通知时,还需要判断pred条件为true才真正苏醒。这种重载形式是为了防止“假醒”
    • wait_for()和wait_until()的返回值:
      • 不提供pred参数的wait_for()和wait_until()的返回值属于以下枚举类:
        • std::cv_statue::timeout——在指定等待指定的时间之后,还没有收到通知,计时器超时
        • std::cv_statue::no_timeout——在等待过程中收到了通知
      • 提供pred参数的wait_for()和wait_until()的返回值为pred判断式的执行结果
    • notify_all_at_thread_exit(cv,ul)全局函数:
      • 用来在其调用者(线程)退出(exit)时调用notify_all()。为此它暂时锁住对应的lock l,后者必须使用所有等待线程共享的一个mutex
      • 为避免死锁,线程调用notify_all_at_thread_exit()之后应该直接退出(exit)。因此这个调用只是为了在通知waiting thread之前先完成清理工作,而且这个清理工作绝不该造成阻塞

    condition_variable_any

     

    • condition_variable_any提供的操作与condition_variable类似(见上图),但是不提供native_handle()和notify_all_at_thread_exit()
    • condition_variable_any不要求使用std::unique_lock对象当做lock
    • 正如C++标准库所言:如果你使用的lock不是标准mutex类型,或者如果你使用标准mutex类型的一个群unique_lock wrapper并搭配condition_variable_any,那么使用者必须确保实现condition_variable_any实例对象所关联之predicate(判断式)的任何必要同步化
    • 事实上该实例对象必须履行所谓的BasicLockable规定,该规定要求提供同步化的lock()和unlock()函数
    展开全文
  • 目录 1、std::condition_variable 2、std::condition_variable::notify_all、std::condition_variable::notify_one 3、std::condition_variable::wait、std::...4、condition_variable_any 1、std::condition_vari

    目录

    1、std::condition_variable

    2、std::condition_variable::notify_all、std::condition_variable::notify_one

    3、std::condition_variable::wait、std::condition_variable::wait_for、std::condition_variable::wait_until

    4、condition_variable_any


    1、std::condition_variable

    A condition variable is an object able to block the calling thread until notified to resume.

    It uses a unique_lock (over a mutex) to lock the thread when one of its wait functions is called. The thread remains blocked until woken up by another thread that calls a notification function on the same condition_variable object.

    Objects of type condition_variable always use unique_lock<mutex> to wait: for an alternative that works with any kind of lockable type, see condition_variable_any

    example:

    #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 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 << '\n';
    }
    
    void go() {
      std::unique_lock<std::mutex> lck(mtx);
      ready = true;
      std::cout << ready << '\n';
      cv.notify_all();
      std::cout << "go end" << '\n';
    }
    
    int main ()
    {
      std::thread threads[10];
      // spawn 10 threads:
      for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_id,i);
    
      std::cout << "10 threads ready to race...\n";
      std::this_thread::sleep_for (std::chrono::seconds(3));
      go();                       // go!
    
      for (auto& th : threads) th.join();
    
      return 0;
    }
    

    possible output:

    代码中涉及了两个新的函数wait()与notify_all(),功能就是单词的表面意思(Wait until notified、notify all)。

    如果去掉go()函数中的“cv.notify_all();”这一行,则执行结果如下:

    很好理解,就是没有接受到通知,就一直在等待。线程进入所谓死锁状态。

    那如果彻底不调用go()函数呢,会是啥结果,执行结果如下:

    2、std::condition_variable::notify_all、std::condition_variable::notify_one

    notify_one define:

    Unblocks one of the threads currently waiting for this condition.

    If no threads are waiting, the function does nothing.

    If more than one, it is unspecified which of the threads is selected.

    example(producer and consumer):

    #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 << '\n';
      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;
    }
    

    possible output:

    但是如果第一个例子中10个线程我们采用的是notify_all(),如果使用notify_one()。看一下所谓的“unspecified which of the threads is selected”究竟是咋回事

    运行结果:

    结果是一个都没唤醒。

    3、std::condition_variable::wait、std::condition_variable::wait_for、std::condition_variable::wait_until

    std::condition_variable::wait define:

    The execution of the current thread (which shall have locked lck's mutex) is blocked until notified.

    At the moment of blocking the thread, the function automatically calls lck.unlock(), allowing other locked threads to continue.

    Once notified (explicitly, by some other thread), the function unblocks and calls lck.lock(), leaving lck in the same state as when the function was called. Then the function returns (notice that this last mutex locking may block again the thread before returning).

    std::condition_variable::wait_for define:

    Wait for timeout or until notified
    The execution of the current thread (which shall have locked lck's mutex) is blocked during rel_time, or until notified (if the latter happens first).

    At the moment of blocking the thread, the function automatically calls lck.unlock(), allowing other locked threads to continue.

    Once notified or once rel_time has passed, the function unblocks and calls lck.lock(), leaving lck in the same state as when the function was called. Then the function returns (notice that this last mutex locking may block again the thread before returning).

    std::condition_variable::wait_until define:

    Wait until notified or time point
    The execution of the current thread (which shall have locked lck's mutex) is blocked either until notified or until abs_time, whichever happens first.

    At the moment of blocking the thread, the function automatically calls lck.unlock(), allowing other locked threads to continue.

    Once notified or once it is abs_time, the function unblocks and calls lck.lock(), leaving lck in the same state as when the function was called. Then the function returns (notice that this last mutex locking may block again the thread before returning).

    4、condition_variable_any

    condition_variable_any 类是 std::condition_variable 的泛化。相对于只在 std::unique_lock<std::mutex> 上工作的 std::condition_variable , condition_variable_any 能在任何满足基本可锁定 (BasicLockable) 要求的锁上工作。

    条件变量语义描述见 std::condition_variable 。

    类 std::condition_variable_any 是标准布局类型 (StandardLayoutType) 。它非可复制构造 (CopyConstructible) 、可移动构造 (MoveConstructible) 、可复制赋值 (CopyAssignable) 或可移动赋值 (MoveAssignable) 。

    若锁是 std::unique_lock ,则 std::condition_variable 可能提供更好的性能。

    展开全文
  • 相关的类包括 std::condition_variable 和 std::condition_variable_any,还有枚举类型std::cv_status。另外还包括函数 std::notify_all_at_thread_exit(),下面分别介绍一下以上几种类型。 std::condition_variable...

    < condition_variable > 头文件主要包含了与条件变量相关的类和函数。相关的类包括 std::condition_variable 和 std::condition_variable_any,还有枚举类型std::cv_status。另外还包括函数 std::notify_all_at_thread_exit(),下面分别介绍一下以上几种类型。

    std::condition_variable 类介绍

    当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。

    std::condition_variable 对象通常使用 std::unique_lock< std::mutex > 来等待,如果需要使用另外的 lockable 类型,可以使用 std::condition_variable_any 类,本文后面会讲到 std::condition_variable_any 的用法。

    首先来看一个简单的例子:

    #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 cv;         // 全局条件变量.
    bool ready = false;                 // 全局标志位.
    
    void do_print_id(int id)
    {
        std::unique_lock <std::mutex> lck(mtx);
        while (!ready)       // 如果标志位不为 true, 则等待...
            cv.wait(lck);    // 当前线程被阻塞, 当全局标志位变为 true 之后,
    
        // 线程被唤醒, 继续往下执行打印线程编号id.
        std::cout << "thread " << id << '\n';
    }
    
    void go()
    {
        std::unique_lock <std::mutex> lck(mtx);
        ready = true;        // 设置全局标志位为 true.
        cv.notify_all();     // 唤醒所有线程.
    }
    
    int main()
    {
        std::thread threads[10];  // spawn 10 threads:
        
        for (int i = 0; i < 10; ++i)
            threads[i] = std::thread(do_print_id, i);
    
        std::cout << "10 threads ready to race...\n";
        go(); // go!
    
        for (auto & th:threads)
            th.join();
    
        return 0;
    }
    

    输出结果:

    10 threads ready to race...
    thread 8
    thread 2
    thread 1
    thread 4
    thread 5
    thread 7
    thread 9
    thread 0
    thread 3
    thread 6
    

    好了,对条件变量有了一个基本的了解之后,现在来看看 std::condition_variable 的各个成员函数。

    std::condition_variable 构造函数

    defaultcondition_variable();
    copy [deleted]condition_variable (const condition_variable&) = delete;

    std::condition_variable 的拷贝构造函数被禁用,只提供了默认构造函数。

    std::condition_variable::wait() 介绍

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

    std::condition_variable 提供了两种 wait() 函数。当前线程调用 wait() 后将被阻塞(此时当前线程应该获得了锁(mutex),不妨设获得锁 lck),直到另外某个线程调用 notify_* 唤醒了当前线程。

    在线程被阻塞时,该函数会自动调用 lck.unlock() 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。另外,一旦当前线程获得通知(notified,通常是另外某个线程调用 notify_* 唤醒了当前线程),wait() 函数也是自动调用 lck.lock(),使得 lck 的状态和 wait 函数被调用时相同。

    在第二种情况下(即设置了 Predicate),只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。因此第二种情况类似以下代码:

    while (!pred()) wait(lck);
    

    请看下面例子:

    #include <iostream>                // std::cout
    #include <thread>                  // std::thread, std::this_thread::yield
    #include <mutex>                   // std::mutex, std::unique_lock
    #include <condition_variable>      // std::condition_variable
    
    std::mutex mtx;
    std::condition_variable cv;
    
    int cargo = 0;
    
    bool shipment_available()
    {
        return cargo != 0;
    }
    
    // 消费者线程.
    void consume(int n)
    {
        for (int i = 0; i < n; ++i) {
            std::unique_lock <std::mutex> lck(mtx);
            cv.wait(lck, shipment_available);
            std::cout << cargo << '\n';
            cargo = 0;
        }
    }
    
    int main()
    {
        std::thread consumer_thread(consume, 10); // 消费者线程.
    
        // 主线程为生产者线程, 生产 10 个物品.
        for (int i = 0; i < 10; ++i) {
            while (shipment_available())
                std::this_thread::yield();
                
            std::unique_lock <std::mutex> lck(mtx);
            cargo = i + 1;
            cv.notify_one();
        }
    
        consumer_thread.join();
    
        return 0;
    }
    

    std::condition_variable::wait_for() 介绍

    在这里插入图片描述
    与 std::condition_variable::wait() 类似,不过 wait_for 可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_for 返回,剩下的处理步骤和 wait() 类似。

    另外,wait_for 的重载版本(predicte(2))的最后一个参数 pred 表示 wait_for 的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞,因此相当于如下代码:

    return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
    

    请看下面的例子,下面的例子中,主线程等待 th 线程输入一个值,然后将 th 线程从终端接收的值打印出来,在 th 线程接受到值之前,主线程一直等待,每个一秒超时一次,并打印一个 “.”:

    #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 do_read_value()
    {
        std::cin >> value;
        cv.notify_one();
    }
    
    int main ()
    {
        std::cout << "Please, enter an integer (I'll be printing dots): \n";
        std::thread th(do_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::cout.flush();
        }
    
        std::cout << "You entered: " << value << '\n';
    
        th.join();
        
        return 0;
    }
    

    std::condition_variable::wait_until 介绍

    在这里插入图片描述
    与 std::condition_variable::wait_for 类似,但是 wait_until 可以指定一个时间点,在当前线程收到通知或者指定的时间点 abs_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_until 返回,剩下的处理步骤和 wait_until() 类似。

    另外,wait_until 的重载版本(predicte(2))的最后一个参数 pred 表示 wait_until 的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞,因此相当于如下代码:

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

    std::condition_variable::notify_one() 介绍

    唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。

    #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 cv;
    
    int cargo = 0; // shared value by producers and consumers
    
    void consumer()
    {
        std::unique_lock < std::mutex > lck(mtx);
        while (cargo == 0)
            cv.wait(lck);
        std::cout << cargo << '\n';
        cargo = 0;
    }
    
    void producer(int id)
    {
        std::unique_lock < std::mutex > lck(mtx);
        cargo = id;
        cv.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;
    }
    

    std::condition_variable::notify_all() 介绍

    唤醒所有的等待(wait)线程。如果当前没有等待线程,则该函数什么也不做。
    请看下面的例子:

    #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 cv;        // 全局条件变量.
    bool ready = false;                // 全局标志位.
    
    void do_print_id(int id)
    {
        std::unique_lock <std::mutex> lck(mtx);
        while (!ready)      // 如果标志位不为 true, 则等待...
            cv.wait(lck);   // 当前线程被阻塞, 当全局标志位变为 true 之后,
        // 线程被唤醒, 继续往下执行打印线程编号id.
        std::cout << "thread " << id << '\n';
    }
    
    void go()
    {
        std::unique_lock <std::mutex> lck(mtx);
        ready = true;      // 设置全局标志位为 true.
        cv.notify_all();   // 唤醒所有线程.
    }
    
    int main()
    {
        std::thread threads[10];
        // spawn 10 threads:
        for (int i = 0; i < 10; ++i)
            threads[i] = std::thread(do_print_id, i);
    
        std::cout << "10 threads ready to race...\n";
        go(); // go!
    
        for (auto & th:threads)
            th.join();
    
        return 0;
    }
    

    std::condition_variable_any 介绍

    与 std::condition_variable 类似,只不过 std::condition_variable_any 的 wait 函数可以接受任何 lockable 参数,而 std::condition_variable 只能接受 std::unique_lockstd::mutex 类型的参数,除此以外,和 std::condition_variable 几乎完全一样。

    std::cv_status 枚举类型介绍

    在这里插入图片描述

    std::notify_all_at_thread_exit

    函数原型为:

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

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

    #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 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 << '\n';
    }
    
    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];
      // spawn 10 threads:
      for (int i=0; i<10; ++i)
        threads[i] = std::thread(print_id,i);
        
      std::cout << "10 threads ready to race...\n";
    
      std::thread(go).detach();   // go!
    
      for (auto& th : threads) th.join();
    
      return 0;
    }
    
    展开全文
  • condition_variable介绍

    2022-04-07 11:58:32
    std::condition_variable std::condition_variable是条件变量。 [Linux](https://www.2cto.com/os/linux/)下使用 Pthread库中的 pthread_cond_*() 函数提供了与条件变量相关的功能。 当 std::condition_variable对象...
  • 包括相关类std::condition_variable和std::condition_variable_any,还有枚举类型std::cv_status。另外还包含函数std::notify_all_at_thread_exit(),以下分别介绍一下以上几种类型。 std::condition_variable 类...
  • Windows的CONDITION_VARIABLE使用CRITICAL_SECTION时,代码如下 #include <Windows.h> #include <iostream> #include <thread> #include <vector> int Test(CONDITION_VARIABLE &cv,...
  • C++11 condition_variable条件变量用法1 什么是条件变量2 condition_variable类定义2.1 wait函数3 condition_variable用法3.1 资源修改线程步骤3.2 资源等待线程步骤4 代码示例4.1 无需notify场景4.2 正常应用场景...
  • 相关的类包括 std::condition_variable 和 std::condition_variable_any,还有枚举类型std::cv_status。另外还包括函数 std::notify_all_at_thread_exit(),下面分别介绍一下以上几种类型。 std::condition_...
  • 文章目录条件变量头文件condition_variable公共方法waitwait(_Lck)流程图示例错误示例:等待前通知,导致无法获得通知wait(_Lck, _Pred)流程图示例:等待后通知示例:等待前通知错误示例:通知线程不加lock可能出现...
  • 一、condition_variable 概述 <condition_variable>是C++标准程序库中的一个头文件,定义了C++11标准中的一些用于并发编程时表示条件变量的类与方法等。 条件变量是并发程序设计中的一种控制结构。多个线程...
  • 对多线程编程有了解的对条件变量(condition_variable)这个概念一定不陌生,相较于另一种常见的同步机制互斥锁,条件变量是一种更高层次的同步手段,互斥锁用于保护一段临界区,也就是各个线程互斥地去访问同一块...
  • 一、std::condition_variable 是条件变量。 wait() 当 std::condition_variable 对象的某个 wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一...
  • C++11并发编程-条件变量(condition_...相关的类包括 std::condition_variable和 std::condition_variable_any,还有枚举类型std::cv_status。另外还包括函数 std::notify_all_at_thread_exit(),下面分别介绍一...
  • 相关的类包括 std::condition_variable 和 std::condition_variable_any,还有枚举类型std::cv_status。另外还包括函数 std::notify_all_at_thread_exit() std::condition_variable::wait() std::condition_...
  • std::condition_variable使用要包含头文件#include <condition_variable> 定义condition_variable对象: std::condition_variable my_cond; condition_variable成员函数wait(): my_cond.wait(my_lock,...
  • (转 )C++11多线程条件变量std::condition_variable详解 目录 std::condition_variable 类介绍 std::condition_variable 构造函数 std::condition_variable::wait() std::condition_variable::wait_for() std...
  • c++11多线程 条件变量 std::condition_variable 类介绍
  • 介绍condition_variable, wait,wait_for 直接上代码如下: #include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::...
  • 包括相关类std::condition_variable和std::condition_variable_any,还有枚举类型std::cv_status。另外还包含函数std::notify_all_at_thread_exit(),以下分别介绍一下以上几种类型。 std::condition_...
  • std::condition_variable的用法 具体用法参照这篇文章,这里只针对其中的一个成员函数 wait() 的用法进行讨论。 成员函数wait()的用法 关于wait()的用法,在这篇文章里说到: (2)、wait:当前线程调用wait()后将被...
  • mutex与condition_variable

    2022-01-27 20:50:56
    mutex、condition_variable
  • 《1》条件变量std::condition_variable、wait()、notify_one 《2》对上述代码之深入思考 《3》notify_all()
  • 条件变量std::condition_variable的作用是阻塞线程,然后等待通知将其唤醒。我们可以通过某个函数判断是否符合某种条件来决定是阻塞线程等待通知还是唤醒线程,由此实现线程间的同步。所以简单来说condition_...
  • c++多线程之condition_variable 简介 等待线程 wait() wait_for wait_until 通知线程 总结 简介 condition_variable的作用是阻塞线程,然后等待通知将其唤醒。我们可以通过某个函数判断是否符合某种条件来决定是阻塞...
  • C++ 中条件变量 std::condition_variable 的使用
  • 线程同步:condition_variable的隐患

    千次阅读 2022-03-22 19:42:54
    线程同步:condition_variable的隐患

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,527
精华内容 22,210
关键字:

condition_variable