zoukankan      html  css  js  c++  java
  • 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...等操作。

  • 相关阅读:
    loj#6433. 「PKUSC2018」最大前缀和(状压dp)
    PKUWC2019游记
    10. Regular Expression Matching
    9. Palindrome Number
    8. String to Integer (atoi)
    7. Reverse Integer
    6. ZigZag Conversion
    5. Longest Palindromic Substring
    4. Median of Two Sorted Arrays
    3. Longest Substring Without Repeating Characters
  • 原文地址:https://www.cnblogs.com/lidabo/p/3796128.html
Copyright © 2011-2022 走看看