muduo源码学习笔记(1)

xiaoxiao2021-03-01  27

前言:

​ 对于muduo库,我觉得,光Linux多线程上提到的一些实现,还是不够的,在base/里面,还有/net里面提供了很多不错的实现,值得去学习,暑假算是看看muduo的百分之八十的源码,并对其进行了一次实现,(剩下的在最近总结的时候,也会开始看看,并实现一遍),对于muduo库,简单谈谈自己对其实现的理解。

用RAII管理你的锁

​ Posix Thread内定义的一系列的mutex函数,但是是基于C语言的,用起来很不方便,需要不断地初始化,销毁p,我们要对其进行一定程度的封装,使得我们不必重复的写初始话,销毁操作,专注在使用上。

​ muduo对锁进行了封装,分别是Mutex、MutexGruad、Condition、CountDownLatch。

Mutex的实现

class MutexLock : boost::noncopyable   {    public:        MutexLock():holder_(0)       {            MCHECK(pthread_mutex_init(&mutex_, NULL));       }        ~MutexLock()       {            assert(holder_ == 0);            MCHECK(pthread_mutex_destroy(&mutex_));       }        bool isLocketByThisThread() const       {            return holder_ == CurrentThread::tid();       }        void assertLocked() const       {            assert(isLocketByThisThread());       }        void lock()       {            MCHECK(pthread_mutex_lock(&mutex_));            assignHolder();       }        void unlock()       {            unassignHolder();            MCHECK(pthread_mutex_unlock(&mutex_));       }        pthread_mutex_t* getPthreadMutex() /* non-const */          {            return &mutex_;       }        private:        void unassignHolder()       {            holder_ = 0;       }        void assignHolder()       {            holder_ = CurrentThread::tid();       }    private:        pthread_mutex_t  mutex_;        pid_t holder_;       // 如果不是同一个线程的加锁和解锁则会失败   };

如果单单只是这样实现mutex,还是太过于麻烦,而且这个锁,我们只能在函数内自动获取自动产生,然后自动析构,会有一定的性能损失。

下面实现了MutexLockGurad用于自动获取锁,加锁和解锁。

   class MutexLockGuard : boost::noncopyable   {    public:        explicit MutexLockGuard(MutexLock& mutex):mutex_(mutex)       {            mutex_.lock();       }        ~MutexLockGuard()       {            mutex_.unlock();       }    private:        MutexLock& mutex_;   };

还有需要注意的就是,在实现的时候,定义了两个宏

// 用宏的方式检查返回值是否为0, #ifdef CHECK_PTHREAD_RETURN_VALUE #ifdef NDEBUG __BEGIN_DECLS extern void __assert_perror_fail (int errnum,                                  const char *file,                                  unsigned int line,                                  const char *function)    __THROW __attribute__ ((__noreturn__)); __END_DECLS #endif ​ #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \                       if (__builtin_expect(errnum != 0, 0))   \                         __assert_perror_fail (errnum, __FILE__, __LINE__, __func__);}) ​ #else  // CHECK_PTHREAD_RETURN_VALUE // 定义一个ret类型的errnum,然后判断errnum是否==0, // (void)(value)作用仅仅就是以显眼的方式让编译器不要给出参数未被使用的警告 #define MCHECK(ret) ({ __typeof__ (ret) errnum = (ret);         \                          assert(errnum == 0); (void) errnum;}) ​ #endif // CHECK_PTHREAD_RETURN_VALUE // 防止错误的用法 #define MutexLockGuard(x) error "Missing guard object name"

没有注释的代码,里面有些小问题,我们可以总结一下

__builtin_expect

​ 提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

​ 函数声明如下

​ long __builtin_expect(long exp, long c);

    exp 为一个整型表达式, 例如: (ptr != NULL)

     c 必须是一个编译期常量, 不能使用变量

    返回值等于 第一个参数 exp

​ 这个函数的语义是:你期望exp表达式的值等于常量c,从而GCC为你优化程序,将符合这个条件的分支放在合适的地方。

__assert_perror_fail (errnum, __FILE__, __LINE__, __func__)

​ 最后会按照FILE :LINE : func : errnum打印出错误,和assert一样,都在编译时候执行,但是好处在于它能按照格式打印出错误在哪里,便于快速排查出错误。

Condition的实现

​ 如果直接使用我们上面实现的Mutex,会出现死锁。

​ 因为在我们使用Conditon的wait的时候:

释放Mutex

阻塞等待

​ 当其它线程调用pthread_cond_signal或pthread_cond_signal,它会重写获取锁。

​ 这个时候,pthread_cond_wait势必会改变pthread_mutex_t和MutexLock:holder的一致性。所以需要在调用pthread_cond_wait的前后添加一些代码去相应的修改*MutexLock::holder,也就是分别调用MutexLock::unassignHolder和MutexLock::assignHolder。MutexLock::UnassignGuard类的作用,就是利用RAII简化对MutexLock::unassignHolder和MutexLock::assignHolder的调用。*

所以在Mutex中我们需要加入unassignGuard类

class Mutex {   ... private:  friend class Condition; ​  class UnassignGuard : boost::noncopyable {   public:    UnassignGuard(MutexLock& owner)     : owner_(owner)   {      owner_.unassignHolder();   } ​    ~UnassignGuard()   {      owner_.assignHolder();   } ​   private:    MutexLock& owner_; };   ... };

这样我们就可以实现Conditon了

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_);   //先将holder_清零,防止出现死锁    MCHECK(pthread_cond_wait(&pcond_, mutex_.getPthreadMutex()));    // 析构的时候,会将holder_恢复 } ​  // 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_)); }  void waitForSeconds(double seconds) { struct timespec abstime; // FIXME: use CLOCK_MONOTONIC or CLOCK_MONOTONIC_RAW to prevent time rewind. clock_gettime(CLOCK_REALTIME, &abstime); ​ const int64_t kNanoSecondsPerSecond = 1000000000; int64_t nanoseconds = static_cast<int64_t>(seconds * kNanoSecondsPerSecond); ​ abstime.tv_sec += static_cast<time_t>((abstime.tv_nsec + nanoseconds) / kNanoSecondsPerSecond); abstime.tv_nsec = static_cast<long>((abstime.tv_nsec + nanoseconds) %     kNanoSecondsPerSecond); ​    MutexLock::UnassignGuard ug(mutex_);    return ETIMEDOUT == pthread_cond_timedwait(&pcond_, mutex_.getPthreadMutex(), &abstime); } ​ private:  MutexLock& mutex_;  pthread_cond_t pcond_; }; ​

CountDownLatch的实现

我们有时对Condition类上面再套一层封装来使用,他就是我们的CountDownLatch类。通过倒计时计数器的方式,设置计数。倒计时未完,CountDownLatch的内部一直处于

Condition类的wait()状态。倒计时完毕,唤醒cond,程序正常执行。所以我们可以给某件即将发生的事设置一个条件,比如至少5个满足才会触发该事件,那么计数值设为5,

等到倒计时为0时,CountDownLatch类就可以让你顺利的去执行该事件了。

所以它主要有两个用法:

​ 1.既可以用于所有子线程主动等待主线程发起“起跑”

​ 2.也可以用于主线哼等待子线程初始化完毕才开始工作

​ class CountDownLatch : boost::noncopyable { public: ​  explicit CountDownLatch(int count) : mutex_(),    condition_(mutex_),    count_(count) { } ​  void wait() {    MutexLockGuard lock(mutex_); while (count_ > 0) {   condition_.wait(); } } ​  void countDown() {    MutexLockGuard lock(mutex_); --count_; if (count_ == 0) {   condition_.notifyAll(); } } ​  int getCount() const {  MutexLockGuard lock(mutex_);  return count_; } ​ private:  mutable MutexLock mutex_;  Condition condition_;  int count_; };

 

 

转载请注明原文地址: https://www.6miu.com/read-4200174.html

最新回复(0)