首页 理论教育 C++STL中mutex模板类

C++STL中mutex模板类

时间:2023-10-25 理论教育 版权反馈
【摘要】:一个mutex类型对象主要用于多线程之间保护数据以及确保数据的同步性。类mutex可以提供非递归互斥对象的唯一占有权。下面使用例16-4来说明类mutex的使用方法。

C++STL中mutex模板类

一个mutex类型对象主要用于线程之间保护数据以及确保数据的同步性。一个可执行程序或者线程从第一次调用lock()开始,直至调用unlock()为止,期间确保该线程归该线程所有。互斥对象可以是递归类型,也可以是非递归类型;可以保证瞬间获取一个或多个线程的对CPU的占有权。互斥类型对象提供唯一的所有权:在同一时刻,仅有一个线程可以拥有互斥对象。无论是递归型还是非递归型,互斥对象均具有此性质。

互斥类型主要是标准库类型std::mutex、std::recursive_mutex、std::timed_mutex和std::recursive_timed_mutex。互斥类型对象必须满足可锁定性能要求。互斥类型包含默认的构造器和析构器。如果互斥类型对象初始化失败,系统会抛出异常(system_error)。互斥类型对象是不能被复制和不能被移动的。

通常,错误条件的错误代码包含以下几种:

•resource_unavailable_try_again。该错误代码代表无效资源,请重新尝试。

•operation_not_permitted。该错误代码代表无操作权限。

•device_or_resource_busy。该错误代码代表其他线程正在占用CPU资源。

•invalid_argument。该错误代码代表该参数无效。

在多线程执行实施过程中,互斥类型对象要提供锁定和解锁两种操作。为证明数据竞争的存在,互斥对象的行为有些类似原子操作。例如,

978-7-111-51399-5-Chapter16-19.jpg

则语句

978-7-111-51399-5-Chapter16-20.jpg

执行该语句之后,该线程即拥有了该互斥对象;该线程可执行相应的操作。一旦操作发生错误,系统会抛出异常(system_error)。错误的代码通常包含以下3种:

•operation_not_permitted。该错误代码代表无操作权限。

•resource_deadlock_would_occur。该错误代码代表可能发生死锁现象。

•device_or_resource_busy。该错误代码代表程序中互斥类型对象已经锁定,阻塞该线程是不可能的。再如,

语句

978-7-111-51399-5-Chapter16-21.jpg

执行该语句之后,该线程尝试获取互斥类型对象的所有权。如果不能获取所有权,try_lock()函数迅速返回,不产生任何作用。即使其他线程没有占有改互斥类型对象时,也有可能尝试锁定(try_lock())失败。又如,

语句

978-7-111-51399-5-Chapter16-22.jpg

该语句执行的前提条件是该线程已经拥有互斥类型m的所有权。执行该语句之后,该互斥类型的所有权即被释放。

1.类mutex

类mutex的声明形式如下:

978-7-111-51399-5-Chapter16-23.jpg

由上述内容可知,该类包含两个构造函数、一个解析函数、一个锁函数(lock())、一个尝试锁函数(try_lock())、一个解锁函数、一个赋值函数operator=()以及native_handle()函数。

类mutex可以提供非递归互斥对象的唯一占有权。当某互斥类型对象被一个线程占有时,另一个线程无法获取该互斥类型对象的所有权,只有等待该线程释放(unlock())互斥类型对象的所有权之后,才能获取该互斥类型对象的所有权。

下面使用例16-4来说明类mutex的使用方法。

例16-4

978-7-111-51399-5-Chapter16-24.jpg

例16-4的执行效果如图16-4所示。

978-7-111-51399-5-Chapter16-25.jpg

图16-4 例16-4的执行效果

2.递归式互斥对象类recursive_mutex

递归式互斥对象类(recursive_mutex)的声明形式如下:

978-7-111-51399-5-Chapter16-26.jpg

由上述内容可知,该类包含两个构造函数、一个析构函数、一个锁函数、一个解锁函数、一个尝试锁函数(try_lock)以及native_handle()函数。

该模板类提供了一个递归式互斥类型,并且具有唯一的所有权。如果线程拥有该递归式互斥对象,另一个线程尝试获取所有权时将失败或者发生阻塞,直到第一个线程完全释放该所有权才能恢复。递归式互斥对象满足互斥类型的所有性能和要求。

通过调用lock()函数或者try_lock(),拥有递归式互斥对象的任意线程将获取额外级别的所有权。单个线程不确定能获得多少个级别的所有权。任意线程一旦获取最大级别的所有权,再调用try_lock()时将失败,如果再调用lock()会抛出system_error类型的异常。通过调用lock()和try_lock(),获取的每个级别的所有权,线程需要调用unlock()解锁。只有该线程的所有级别的所有权全部释放之后,其他线程才能获取获得所有权。(www.xing528.com)

如果线程中的互斥对象遭到破坏,或者当拥有线程所有权时发生线程终止的情况,那么程序会发生不确定行为。

互斥对象的主要职责是保护共享数据。与独占式互斥量不同的是,同一个线程在互斥量没有解锁的情况下可以再次进行加锁,不过它们的加解锁次数需要一致。递归式互斥量可能用得比较少些。

通过以上论述可知,调用线程“拥有”一个recursive_mutex了一段时间,开始时,它成功地调用lock()或try_lock()函数。在此期间,该线程可能再次调用lock()或try_lock()。所有权的时限结束时,线程会根据匹配的次数相应地调用unlock()。

例16-5

978-7-111-51399-5-Chapter16-27.jpg

978-7-111-51399-5-Chapter16-28.jpg

例16-5的执行效果如图16-5所示。

978-7-111-51399-5-Chapter16-29.jpg

图16-5 例16-5的执行效果

由以上示例可知,递归式互斥量可以代替普通互斥量。如果在程序中去掉互斥量的使用,程序中两个线程的竞争将是无序的,程序的输出也是乱序的。

3.定时式互斥对象类timed_mutex

定时式互斥对象类的声明形式如下:

978-7-111-51399-5-Chapter16-30.jpg

该类和普通互斥量对象相比,增加了两个函数:try_lock_for()和try_lock_until()。

try_lock_for()函数接受一个时间范围作为参数,表示在这一段时间范围之内,若线程没有获得锁,则该线程被阻塞相应的时间长度;若在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁。若超时(即在指定时间内还是没有获得锁),则返回false。

try_lock_until()函数则接受一个时间点(绝对时间)作为参数,表示在指定时间点未到来之前若线程没有获得锁,则被阻塞住;若在此期间其他线程释放了锁,则该线程可以获得对互斥量的锁。如果超时(即在指定时间内还是没有获得锁),则返回false。

定时式互斥对象提供了一种非递归式互斥量,该互斥量具有唯一的被占有权。如果一个线程拥有一个定时式互斥对象,那么另一个线程尝试获取该互斥对象的所有权时将发生失败(tyr_lock)或发生阻塞(try_lock_for和try_lock_until),直到拥有互斥对象权限的线程完全释放该所有权时,另一个线程才能获取该对象的所有权。

当发生以下情况时,程序可能会发生不确定性行为:

•任意线程拥有的定时式互斥对象发生破坏。

•当互斥对象尚没有被解锁时,线程发生终止或者结束。

例16-6

978-7-111-51399-5-Chapter16-31.jpg

978-7-111-51399-5-Chapter16-32.jpg

例16-6的执行效果如图16-6所示。

978-7-111-51399-5-Chapter16-33.jpg

图16-6 例16-6的执行效果

4.递归式定时互斥对象类recursive_timed_mutex

递归式定时互斥对象类的声明形式如下:

978-7-111-51399-5-Chapter16-34.jpg

由以上内容可知,递归式定时互斥对象类的成员函数和定时式互斥对象类的成员函数基本相同——既有递归式互斥的多锁作用,又有定时互斥的作用。

下面以例16-7来说明递归式定时互斥对象类的使用方法。

例16-7

978-7-111-51399-5-Chapter16-35.jpg

978-7-111-51399-5-Chapter16-36.jpg

例16-7的执行效果如图16-7所示。

978-7-111-51399-5-Chapter16-37.jpg

图16-7 例16-7的执行效果

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈