条件变量和互斥变量都是boost库中被封装的类。
条件变量是thread库提供的一种等待线程同步的机制,可实现线程间的通信,它必须与互斥量配合使用,等待另一个线程中某个事件发生后本线程才能继续执行。
互斥量是一种用于多线程编程的手段,它可以在多线程编程中防止多个线程同时操作共享资源[或称为临界区 ]。思想为:在每个线程开始的第一条语句使用获取互斥变量“锁有权”的语句,一旦一个线程[线程1]锁住了互斥量,那么其它线程只有等待线程1解锁互斥量后且另一线程[线程2]又获取到互斥变量的“锁有权”后才能运行这个线程[线程2]后面的代码。
[1]互斥变量的使用让线程整个整个的运行。
[2]如果“同时”创建多线程,则首先获取到互斥变量“锁有权”的线程是不定的。
有时候我们需要先运行某个线程一会儿后才能运行另一个线程,或者是需要先运行某个线程[线程1]一会儿后必须运行另一个线程[线程2]后线程1才能够继续运行。C++ boost库对条件变量的封装 就是来解决这些问题的。
C++boost 的thread库提供两种条件变量对象condition_variable及condition_variable_any,由于后者能够适用于更广泛的互斥量类型,所以一般用condition_variable_any,其类摘要如下:
class condition_variable_any { public: void notify_one(); void notify_all(); template <typename lock_type> void wait(lock_type &lock); template<typename lock_type, typename predicate_type> void wait(lock_type &lock,predicate_type predicate); template<typename lock_type, typename duration_type> bool timed_wait(lock_type &lock, duration_type const& rel_time); }
条件变量condition_variable_any中只封装了]notify_one(),notify_all()和wait()系列两类函数。
拥有条件变量的线程先锁定互斥量,然后循环检查某个条件,如果条件不满足,那么就调用成员函数wait()等至条件满足。而其它线程处理条件变量要求的条件,当条件满足时调用它的成员函数notify_one()或notify_all(),以通知所有正在等待条件变量的线程停止等待继续执行。
其实这一小段话就将condition_variable_any类摘要的使用机制给描述了,但是不得不承认我并没有读懂,所以还得继续写程序验证一下条件变量到底封装了怎么一个机制。
[1]定义生存期和作用域能够应用于多线程中的条件变量对象和互斥变量对象。条件变量对象的个数据程序需求制定。
condition_variable_any con_var_1; condition variable_anu con_var_2; mutex mu; |
[2]每个线程对应函数的第一条语句使用锁定互斥量的语句,最后一条语句使用解锁互斥量的语句。【如果不使用条件变量来实现线程通信,则如此就能够使多线程中单个线程一个接连一个的运行,运行时间和场合的不同,各线程运行的顺序也不同】
void thread_fun_1() { mu.lock(); …… mu.unlock(); } |
void thread_fun_2() { mu.lock(); …… mu.unlock(); } |
[3]thread_fun_1()输出”123456789”字符串的奇数数字,thread_fun_2()输出字符串的偶数数字,并且用条件变量和多线程来实现。
void thread_fun_1( const string &str ) { mu.lock(); while ( i < str.length() ) { while( !(i % 2) ) { con_var_1.wait( mu ); } if ( i < str.length() ) { cout << "p1:" << str[i] <<"\n"; } ++i; //i++后一定是奇数,但是如果是此线程先运行,则第一次通知print_2.wait()时它都不曾等待过 con_var_2.notify_one(); } mu.unlock(); } |
void thread_fun_2( const string &str ) { mu.lock(); while ( i < str.length() ) { while( i % 2 ) { con_var_2.wait( mu ); } if ( i < str.length() ) { cout << "p2:" << str[i] <<"\n"; } ++i; //i++后一定是奇数,但是如果是此线程先运行,则第一次通知print_2.wait()时它都不曾等待过 con_var_1.notify_one(); } mu.unlock(); } |
其中i可以是两个线程函数都能够访问的全局变量。
[4]在主程序中创建两个线程来跑这两个线程函数。
thread th_1(thread_fun_1, “123456789”); thread th_2(thread_fun_2, ”123456789”); |
可以使用getchar()函数让运行窗口停留,程序运行效果为目标期待的样子【前提是知道boost库的使用需要包含的头文件及开发环境的配置】:
Figure1:条件变量应用于多线程运行结果
当不懂得一个程序运行的结果时,那就老老实实从最基本的机制用自己的脑袋将有必要的那部分程序运行一遍。我的笨方法。
其实这个机制在程序执行分析时已经完全的体现了。不过看起来挺长的样子,语言描述也不流畅。估计我今后也懒得看,还是整理一个比较简洁的版本来概括一下条件变量使用的机理。
总而言之,条件变量是一种逻辑控制,就是给咱提供了两个函数:wait函数用于发出信号并等待[还有unlock/lock机制],notify_one就是通知wait函数不用等了醒来返回吧。当在每个线程中都使用条件变量提供的这种机制时,逻辑上就比较复杂了,但是对于cpu来说很好分清,只要根据运行结果不断的调试,总会调到自己期望的逻辑效果。
如果在程序中创建了多个线程,不使用互斥变量来约束各线程的运行顺序,那么当某个线程被定义时它就开始运行,在多线程的情况下,各线程以极短的时间交替运行着,可以认为在“同一时间”他们都在运行。
对于使用相同互斥变量的多个线程来说,只有获取到互斥量“锁有权”的线程才能运行,否则就会被阻塞直到获取到了互斥量的“锁有权”。所以,线程是被整个整个依次运行完毕的。而且很有可能是这样:不同时间的运行对应着不同次序的运行,获取互斥变量的顺序不同造成的。
在互斥变量的基础之上,使用条件变量就是能实现标题的内容。线程既不是“同时”运行也不是会因为运行环境的不同而造成各线程运行顺序的不同。
(1)~(3)是循序渐进的一个过程。
Thread Note Over。