C++并发编程之线程同步 std::condition_variable用法总结

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              // std::cout
#include                // std::thread
#include                 // std::mutex, std::unique_lock
#include    // 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_的线程运行就可以了。具体的就是各种等待的方式不一样罢了。

你可能感兴趣的:(C/C++,c++)