C++ BOOST库 条件变量[多线程通信]机制 [大三四八九月实习]

1相关理念

(1)类名

条件变量和互斥变量都是boost库中被封装的类。

 

(2)条件变量

条件变量是thread库提供的一种等待线程同步的机制,可实现线程间的通信,它必须与互斥量配合使用,等待另一个线程中某个事件发生后本线程才能继续执行。

 

(3)互斥变量

互斥量是一种用于多线程编程的手段,它可以在多线程编程中防止多个线程同时操作共享资源[或称为临界区 ]。思想为:在每个线程开始的第一条语句使用获取互斥变量“锁有权”的语句,一旦一个线程[线程1]锁住了互斥量,那么其它线程只有等待线程1解锁互斥量后且另一线程[线程2]又获取到互斥变量的“锁有权”后才能运行这个线程[线程2]后面的代码。

 

(4)互斥量有可能的缺点

[1]互斥变量的使用让线程整个整个的运行。

[2]如果“同时”创建多线程,则首先获取到互斥变量“锁有权”的线程是不定的。

 

(5)条件变量的简单应用场景

有时候我们需要先运行某个线程一会儿后才能运行另一个线程,或者是需要先运行某个线程[线程1]一会儿后必须运行另一个线程[线程2]后线程1才能够继续运行。C++ boost库对条件变量的封装 就是来解决这些问题的。

 

2 条件变量的使用机制总结

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()系列两类函数。

 

2.1参考书对条件变量的描述

拥有条件变量的线程先锁定互斥量,然后循环检查某个条件,如果条件不满足,那么就调用成员函数wait()等至条件满足。而其它线程处理条件变量要求的条件,当条件满足时调用它的成员函数notify_one()notify_all(),以通知所有正在等待条件变量的线程停止等待继续执行。

其实这一小段话就将condition_variable_any类摘要的使用机制给描述了,但是不得不承认我并没有读懂,所以还得继续写程序验证一下条件变量到底封装了怎么一个机制。

 

2.2实践总结笔记

(1)简单使用条件变量步骤

[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库的使用需要包含的头文件及开发环境的配置】:

C++ BOOST库 条件变量[多线程通信]机制 [大三四八九月实习]_第1张图片

 

Figure1:条件变量应用于多线程运行结果

 

(2)程序执行分析[wait与notify]

当不懂得一个程序运行的结果时,那就老老实实从最基本的机制用自己的脑袋将有必要的那部分程序运行一遍。我的笨方法。

  • [1]两个线程不管哪一个先获取到互斥变量mu的“锁有权”,另一个线程的运行就会被阻塞。如线程th_2先获取到互斥变量mu的“锁有权”,那么就进入thread_fun_2内,此时th_1不拥有互斥变量的“锁有权”就会被其线程函数内的mu.lock()语句阻塞。
  • [2]thread_fun_2打印一次字符到屏幕之上后就会进入以 i%2为条件的while循环中执行”con_var_2.wait( mu);”语句。根据条件变量wait()函数的功能,th_2线程就会开始睡眠[就停留在”con_var_2.wait( mu );”语句处],同时wait(mu)函数还将unlock互斥量mu,在时刻mu.lock(mu)[等待锁定互斥量mu]的线程th_1立马获得互斥量的“锁有权”,然后开始执行线程th_1对应函数thread_fun_1内的内容[如果th_1内没有使用wait代码,那么此次线程th_1将会执行完毕,不是con_var_2.notify_one()函数一执行和th_2con_var_2.wait()完成通信之后就会立即返回到th_2对应函数thread_fun_2中继续执行,而是会将th_1线程执行完之后再回到th_2(thread_fun_2)con_var_2.wait()语句处 ,此时con_var_2.wait()函数返回,然后wait函数将再lock互斥变量mu。然后再判断while中的条件,如果条件为假则执行下一条语句,如果while中的条件依然为真,那么重新调用con_var_2.wait()函数继续等待]
  • [3]th_1d对应函数打印一个偶数数字到屏幕之后,进入while之中执行con_var_1.wait( mu )语句,由于此时线程th_2”con_var_2.wait( mu );”已经收到过”con_var_1.notify_one();”的通信,所以th_2继续lock  mu,然后执行下一段代码。在++i后,再通知th_1中的con_var_1.wait(mu )函数,等th_2再次发生等待时,th_1中的wait函数属性,重新锁定互斥变量mu,继续执行。如此反复执行代码,直到两个线程都执行完毕为止。
  • [4]不过也发现了一个问题:两个线程初次使用notify_one()函数时,对方线程内都还没有使用过wait函数,那么notify_one()函数初次使用时是个什么状况?或许condition_variable_any在封装了处理这个问题的代码。I do not know now

 

(3)条件变量封装机理[wait与notify]

其实这个机制在程序执行分析时已经完全的体现了。不过看起来挺长的样子,语言描述也不流畅。估计我今后也懒得看,还是整理一个比较简洁的版本来概括一下条件变量使用的机理。

  • [1]条件变量类的使用体现在waitnotify_one两个函数之上
  • [2]当在一个线程中用一个条件变量对象1调用wait函数时,它将起到暂时unlock多线程间供用的互斥变量而让本线程陷入等待之中。【由于对互斥变量的unlock,其它的线程会锁定互斥变量】,等其它线程执行完毕,再用条件变量对象1调用notify_one函数时,wait函数就会苏醒,并重新lock互斥变量,继续执行本线程。

总而言之,条件变量是一种逻辑控制,就是给咱提供了两个函数:wait函数用于发出信号并等待[还有unlock/lock机制]notify_one就是通知wait函数不用等了醒来返回吧。当在每个线程中都使用条件变量提供的这种机制时,逻辑上就比较复杂了,但是对于cpu来说很好分清,只要根据运行结果不断的调试,总会调到自己期望的逻辑效果。

 

3使用条件变量的基础

(1)纯粹的多线程运行

如果在程序中创建了多个线程,不使用互斥变量来约束各线程的运行顺序,那么当某个线程被定义时它就开始运行,在多线程的情况下,各线程以极短的时间交替运行着,可以认为在“同一时间”他们都在运行。

 

(2)使用互斥变量约束线程的运行

对于使用相同互斥变量的多个线程来说,只有获取到互斥量“锁有权”的线程才能运行,否则就会被阻塞直到获取到了互斥量的“锁有权”。所以,线程是被整个整个依次运行完毕的。而且很有可能是这样:不同时间的运行对应着不同次序的运行,获取互斥变量的顺序不同造成的。

 

(3)规定线程的运行顺序

在互斥变量的基础之上,使用条件变量就是能实现标题的内容。线程既不是“同时”运行也不是会因为运行环境的不同而造成各线程运行顺序的不同。

1)~(3)是循序渐进的一个过程。

 

Thread Note Over。

你可能感兴趣的:(C++ BOOST库 条件变量[多线程通信]机制 [大三四八九月实习])