Boost Thread学习笔记二

除了thread,boost::thread另一个重要组成部分是mutex,以及工作在mutex上的boost::mutex::scoped_lock、condition和barrier,这些都是为实现线程同步提供的。

mutex
boost提供的mutex有6种:
boost::mutex
boost::try_mutex
boost::timed_mutex
boost::recursive_mutex
boost::recursive_try_mutex
boost::recursive_timed_mutex
下面仅对boost::mutex进行分析。
mutex类是一个CriticalSection(临界区)封装类,它在构造函数中新建一个临界区并InitializeCriticalSection,然后用一个成员变量
void* m_mutex;
来保存该临界区结构。
除 此之外,mutex还提供了do_lock、do_unlock等方法,这些方法分别调用EnterCriticalSection、 LeaveCriticalSection来修改成员变量m_mutex(CRITICAL_SECTION结构指针)的状态,但这些方法都是private的,以防止我们直接对mutex进行锁操作,所有的锁操作都必须通过mutex的友元类detail::thread::lock_ops<mutex>来完成,比较有意思的是,lock_ops的所有方法:lock、unlock、trylock等都是static的,如lock_ops<Mutex>::lock的实现:

 1 template <typename Mutex>
 2 class lock_ops : private noncopyable
 3 {
 4 
 5 public:
 6     static void lock(Mutex& m)
 7     {
 8         m.do_lock();
 9     }
10 
11 }

boost::thread的设计者为什么会这么设计呢?我想大概是:
1、boost::thread的设计者不希望被我们直接操作mutex,改变其状态,所以mutex的所有方法都是private的(除了构造函数,析构函数)。
2、虽然我们可以通过lock_ops来修改mutex的状态,如:

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <boost/thread/detail/lock.hpp>
 4 
 5 int main()
 6 {
 7     boost::mutex mt;
 8     //mt.do_lock();        // Error! Can not access private member!
 9 
10     boost::detail::thread::lock_ops<boost::mutex>::lock(mt);
11 
12     return 0;
13 }

但是,这是不推荐的,因为mutex、scoped_lock、condition、barrier是一套完整的类系,它们是相互协同工作的,像上面这么操作没有办法与后面的几个类协同工作。
scoped_lock
上面说过,不应该直接用lock_ops来操作mutex对象,那么,应该用什么呢?答案就是scoped_lock。与存在多种mutex一样,存在多种与mutex对应的scoped_lock:

scoped_lock
scoped_try_lock
scoped_timed_lock

这里我们只讨论scoped_lock。
scoped_lock是定义在namespace boost::detail::thread下的,为了方便我们使用(也为了方便设计者),mutex使用了下面的typedef:
typedef detail::thread::scoped_lock<mutex> scoped_lock;
这样我们就可以通过:
boost::mutex::scoped_lock
来使用scoped_lock类模板了。
由于scoped_lock的作用仅在于对mutex加锁/解锁(即使mutex EnterCriticalSection/LeaveCriticalSection),因此,它的接口也很简单,除了构造函数外,仅有lock/unlock/locked(判断是否已加锁),及类型转换操作符void*,一般我们不需要显式调用这些方法,因为scoped_lock的构造函数是这样定义的:

1 explicit scoped_lock(Mutex& mx, bool initially_locked=true)
2     : m_mutex(mx), m_locked(false)
3 {
4     if (initially_locked) lock();
5 }

注:m_mutex是一个mutex的引用。
因此,当我们不指定initially_locked参数构造一个scoped_lock对象 时,scoped_lock会自动对所绑定的mutex加锁,而析构函数会检查是否加锁,若已加锁,则解锁;当然,有些情况下,我们可能不需要构造时自动 加锁,这样就需要自己调用lock方法。后面的condition、barrier也会调用scoped_lock的lock、unlock方法来实现部 分方法。
正因为scoped_lock具有可在构造时加锁,析构时解锁的特性,我们经常会使用局部变量来实现对mutex的独占访问。

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <iostream>
 4 
 5 boost::mutex io_mutex;
 6 
 7 void count()    // worker function
 8 {
 9     for (int i = 0; i < 10; ++i)
10     {
11         boost::mutex::scoped_lock lock(io_mutex);
12         std::cout << i << std::endl;
13     }
14 }
15 
16 int main(int argc, char* argv[])
17 {
18     boost::thread thrd1(&count);
19     boost::thread thrd2(&count);
20     thrd1.join();
21     thrd2.join();
22 
23     return 0;
24 }

在每次输出信息时,为了防止整个输出过程被其它线程打乱,通过对io_mutex加锁(进入临界区),从而保证了输出的正确性。
在使用 scoped_lock时,我们有时候需要使用全局锁(定义一个全局mutex,当需要独占访问全局资源时,以该全局mutex为参数构造一个 scoped_lock对象即可。全局mutex可以是全局变量,也可以是类的静态方法等),有时候则需要使用对象锁(将mutex定义成类的成员变 量),应该根据需要进行合理选择。
Java的synchronized可用于对方法加锁,对代码段加锁,对对象加锁,对类加锁(仍然是对象级 的),这几种加锁方式都可以通过上面讲的对象锁来模拟;相反,在Java中实现全局锁好像有点麻烦,必须将请求封装到类中,以转换成上面的四种 synchronized形式之一。

condition
condition的接口如下:

 1 class condition : private boost::noncopyable   // Exposition only
 2 {
 3 public:
 4   // construct/copy/destruct
 5   condition();
 6   ~condition();
 7 
 8   // notification
 9   void notify_one();
10   void notify_all();
11 
12   // waiting
13   template<typename ScopedLock> void wait(ScopedLock&);
14   template<typename ScopedLock, typename Pred> void wait(ScopedLock&, Pred);
15   template<typename ScopedLock>
16     bool timed_wait(ScopedLock&, const boost::xtime&);
17   template<typename ScopedLock, typename Pred>
18     bool timed_wait(ScopedLock&, Pred);
19 };

其中wait用于等待某个condition的发生,而timed_wait则提供具有超时的wait功能,notify_one用于唤醒一个等待该condition发生的线程,notify_all则用于唤醒所有等待该condition发生的线程。

由于condition的语义相对较为复杂,它的实现也是整个boost::thread库中最复杂的(对Windows版本而言,对支持pthread的版本而言,由于pthread已经提供了pthread_cond_t,使得condition实现起来也十分简单),下面对wait和notify_one进行简要分析。
condition内部包含了一个condition_impl对象,由该对象执行来处理实际的wait、notify_one...等操作。

时间: 2024-10-16 03:40:15

Boost Thread学习笔记二的相关文章

Boost Thread学习笔记三

下面先对condition_impl进行简要分析.condition_impl在其构造函数中会创建两个Semaphore(信号量):m_gate.m_queue,及一个Mutex(互斥体,跟boost::mutex类似,但boost::mutex是基于CriticalSection<临界区>的):m_mutex,其中:m_queue相当于当前所有等待线程的等待队列,构造函数中调用CreateSemaphore来创建Semaphore时,lMaximumCount参数被指定为(std::nume

Boost Thread学习笔记五

多线程编程中还有一个重要的概念:Thread Local Store(TLS,线程局部存储),在boost中,TLS也被称作TSS,Thread Specific Storage.boost::thread库为我们提供了一个接口简单的TLS的面向对象的封装,以下是tss类的接口定义: class tss{public:    tss(boost::function1<void, void*>* pcleanup);    void* get() const;    void set(void*

Boost Thread学习笔记四

barrierbarrier类的接口定义如下:  1 class barrier : private boost::noncopyable   // Exposition only 2 { 3 public: 4   // construct/copy/destruct 5   barrier(size_t n); 6   ~barrier(); 7  8   // waiting 9   bool wait();10 }; barrier类为我们提供了这样一种控制线程同步的机制:前n - 1次

C#学习笔记(二)

笔记 C#学习笔记(二) write by cash(天下第七)2002.01.20版权所有,翻录不究cashcao@msn.com 选择 我身上携带着精神.信仰.灵魂 思想.欲望.怪癖.邪念.狐臭它们寄生于我身体的家 我必须平等对待我的每一位客人-----------伊沙:<原则> 我的名字是cash,所以我很功利主义:我的星像是Leo,所以我很大男人主义:我的语言是C#,所以我有点儿拿不定主义. /* 你能看得出来,这不是一篇正规的技术文章,所以若你不小心从里边读到了一个爱情故事,可不要奇

kvm虚拟化学习笔记(二)之linux kvm虚拟机安装

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://koumm.blog.51cto.com/703525/1289627 KVM虚拟化学习笔记系列文章列表 ---------------------------------------- kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51cto.com/703525/1288795 kvm虚拟化学习笔记(二)之linuxkvm虚拟机安装htt

Bootstrap3学习笔记(二)之排版_javascript技巧

在上篇文章给大家介绍了BootStrap3学习笔记(一)之网格系统 对于标题,Bootstrap已经修改了h1--h6的样式,如果需要副标题,还可以在其中使用small标记 <h1>h1. Bootstrap heading <small>Secondary text</small></h1> <h2>h2. Bootstrap heading <small>Secondary text</small></h2>

kvm虚拟化学习笔记(二十一)之KVM性能优化学习笔记

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://koumm.blog.51cto.com/703525/1606422 本学习笔记系列都是采用CentOS6.x操作系统,KVM虚拟机的管理也是采用virsh方式,网上的很多的文章都基于ubuntu高版本内核下,KVM的一些新的特性支持更好,本文只是记录了CentOS6.x系列操作系统下KVM优化的点,有很多都是默认支持开启了的,除了采用virtio方式的磁盘IO,与网络IO接口

MyBatis学习笔记(二)之关联关系_java

今天主要学习的关联关系是一对一关系与一对多关系. 一.一对一关系 还是通过例子来解释说明.(一个妻子对应一个丈夫). 1)数据库信息 create table t_wife( id int primary key auto_increment, wife_name varchar(), fk_husband_id int ); create table t_husband( id int primary key auto_increment, husband_name varchar() );

Java的“对象思想”学习笔记[二]

笔记|对象 在这篇文章里我将介绍一些Java对于对象的基本处理模式的问题. 首先看一个有关对象相等性(Object equivalence)的例子: 先看下面的代码: public class EqualTest1{ public static void main(String[] args){ Integer n1 = new Integer(20); Integer n2 = new Integer(20); System.out.println(n1 = = n2); System.out