Muduo之封装Mutex和Condition

必要的预备知识:
Linux提供的进行同步原语解释

1、RAII手法

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
RAII的一般做法:在对象构造时获取资源(互斥量上锁),接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源(互斥量解锁,刚好声明周期和资源访问周期等同)。借此,我们实际上把管理一份资源的责任托管给了一个对象。使得不需要显式地释放资源并且对象所需的资源在其生命期内始终保持有效。以下封装Mutex采用了RAII手法。

2、Singleton模式

单件模式:保证一个类仅有一个示例,并提供一个全局访问点。对一些类来说,只有一个实例是很重要的。虽然系统中可以有许多打印机,但却只应该有一个打印假脱机 (printer spooler ),只应该有一个文件系统和一个窗口管理器。一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。一个更好的办法是,让类自身负责保存它的唯一实例。
实现要点:
1、避免构造、拷贝构造和赋值运算符。唯一实例化。
2、通过接口提供访问点。全局访问节点。

3、封装mutex

MutexLock封装Linux底层pthread_mutex_t 实现临界区,然后MutexLockGuard通过RAII手法封装MutexLock。MutexLockGuard封装临界区的进入和退出,一般将MutexLockGuard创建与栈上,它的作用域刚好等于临界区,避免的人为手动解锁和姐解锁,全部交给编译器自动调用。

#include
#include
#include
#define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \
                       assert(errnum == 0); (void) errnum;})//验证系统调用返回值的宏
class MutexLock : boost::noncopyable{//禁止拷贝和赋值
public:
    MutexLock():holder_(0){
        MCHECK(pthread_mutex_init(&mutex_  , NULL));//构造初始化,互斥量属性默认
    }
    ~MutexLock(){//析构删除互斥量
        assert(holder_ == 0);
        MCHECK(pthread_mutex_destroy(&mutex_));
    }
    bool isLockedByThisThread(){
        return holder_ == CurrentThread::tid();
    }
    void assertLocked(){
        assert(isLockedByThisThread());
    }
    void lock(){//仅供MutexLockGuard调用
        MCHECK(pthread_mutex_lock(&mutex_));//锁定
        holder_ = CurrentThread::tid();//获取锁进程ID,静态方法
    }
    void unlock(){//仅供MutexLockGuard调用
        holder_ = 0;
        MCHECK(pthread_mutex_unlock(&mutex_));//解锁
    }
    pthread_mutex_t* getPthreadMutex(){//仅供Condition调用
        return &mutex_;//返回互斥量地址
    }

private:
  friend class Condition;

    class UnassignGuard : boost::noncopyable//私有类
    {
     public:
      UnassignGuard(MutexLock& owner)
        : owner_(owner)
      {
        owner_.unassignHolder();
      }

      ~UnassignGuard()
      {
        owner_.assignHolder();
      }
      MutexLock& owner_;
    };
    void unassignHolder()
    {
      holder_ = 0;
    }
    void assignHolder()
    {
      holder_ = CurrentThread::tid();
    }

    pthread_mutex_t mutex_;//互斥量,内部调用变量名加_
    pid_t holder_;//存储进程ID
};

class MutexLockGuard : boost::noncopyable
{
public:
  explicit MutexLockGuard(MutexLock& mutex) : mutex_(mutex)//构造加锁,且禁止通过此构造函数类型转换
  {
    mutex_.lock();
  }
  ~MutexLockGuard()//析构释放锁
  {
    mutex_.unlock();
  }
private:
  MutexLock& mutex_;
};

#define MutexLockGuard(x) error "Missing guard object name"
/*
防止遗漏对象名,而产生临时对象的错误。
MutexLockGuard(mutex);//将构建临时无名对象,构造之后立马析构,生命周期很短,并没有锁住。只有含有对象名,其生命周期才和临界区一样。
*/

4、封装condition

封装条件变量

#include
#include
#include "mutex.h"
class Condition : boost::noncopyable{
public:
    explicit Condition(MutexLock& mutex)
    : mutex_(mutex)
    {
        MCHECK(pthread_cond_init(&pcond_, NULL));//条件变量初始化
    }

    ~Condition()
    {
        MCHECK(pthread_cond_destroy(&pcond_));//条件变量摧毁
    }

    void wait()
    {
        MutexLock::UnassignGuard ug(mutex_);//互斥锁
        MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));//等待,直到,被通知
    }

    // returns true if time out, false otherwise.
    bool waitForSeconds(double seconds);//超时等待

    void notify()
    {
        MCHECK(pthread_cond_signal(&pcond_));//通知等待
    }

    void notifyAll()
    {
        MCHECK(pthread_cond_broadcast(&pcond_));//广播通知,通知所有等待线程
    }
private:
    MutexLock& mutex_;
    pthread_cond_t pcond_;
};

//condition.cpp
#include "condition.h"
#include 
// returns true if time out, false otherwise.
bool Condition::waitForSeconds(double seconds)
{
  struct timespec abstime;
  clock_gettime(CLOCK_REALTIME, &abstime);//获取系统实时时间

  const int64_t kNanoSecondsPerSecond = 1000000000;//谷歌C++常量定义规范
  int64_t nanoseconds = static_cast(seconds * kNanoSecondsPerSecond);//将传入参数全部转换成ns

  abstime.tv_sec += static_cast((abstime.tv_nsec + nanoseconds) / kNanoSecondsPerSecond);
  abstime.tv_nsec = static_cast<long>((abstime.tv_nsec + nanoseconds) % kNanoSecondsPerSecond);
  //计算函数返回时候的绝对时间。将时间转换成为s和ns并附加在原始时间上面

  MutexLock::UnassignGuard ug(mutex_);//设置mutex_的成员
  return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime);//超时返回,则为true,否则为false
}

Condition变量提供了比较原始的同步机制,实际上使用CountDownLatch,来提供一个通知,多个等待的模式。

class CountDownLatch : boost::noncopyable
{
 public:

  explicit CountDownLatch(int count);

  void wait();

  void countDown();

  int getCount() const;

 private:
  mutable MutexLock mutex_;
  Condition condition_;
  int count_;
};

5、线程安全的Singleton实现

很基本的单件实现方法,需要介绍的就是通过pthread_once实现了多线程安全,这是有操作系统保证的。

template<typename T>
class Singleton : boost::noncopyable//避免拷贝和赋值
{
 public:
  static T& instance()//提供全局访问接口
  {
    pthread_once(&ponce_, &Singleton::init);//多线程中可以保证init仅仅调用一次,也就是初始化一个单例
    assert(value_ != NULL);
    return *value_;
  }

 private:
  Singleton();//避免构造
  ~Singleton();//避免析构,提供唯一实例

  static void init()
  {
    value_ = new T();
    atexit(destroy);//注册终止处理函数,进程结束时候,destroy被自动调用
  }

  static void destroy()
  {
    typedef char T_must_be_complete_type[sizeof(T) == 0 ? -1 : 1];
    T_must_be_complete_type dummy; 
    (void) dummy;
//定义一个char数组类型 T_MUST_BE_COMPELET_TYPE,仅仅为了防止客户错误使用。
//char[-1]---如果T只声明没有定义-不完全类型,此时编译器报错,提醒客户检查传入参数。
//char[1]--T是完全类型,即有定义,那么可以安全delete操作,
    delete value_;
    value_ = NULL;
  }

 private:
  static pthread_once_t ponce_;//静态对象
  static T*             value_;//必须为静态对象,保证此类仅仅拥有这一份拷贝。满足唯一实例
};

template<typename T>
pthread_once_t Singleton::ponce_ = PTHREAD_ONCE_INIT;

template<typename T>
T* Singleton::value_ = NULL;
}

你可能感兴趣的:(Muduo源代码分析)