Boost Thread 临界区 mutex

除了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 ...等操作。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值