Muduo之Mutex、Condition、CountDownlatch解析及例子讲解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014303647/article/details/88398641

首先看看Mutex的源码

namespace muduo
{

// Use as data member of a class, eg.
//
// class Foo
// {
//  public:
//   int size() const;
//
//  private:
//   mutable MutexLock mutex_;
//   std::vector<int> data_ GUARDED_BY(mutex_);
// };
class CAPABILITY("mutex") MutexLock : noncopyable
{
 public:
  MutexLock()
    : holder_(0)
  {
    MCHECK(pthread_mutex_init(&mutex_, NULL));
  }

  ~MutexLock()
  {
    assert(holder_ == 0);
    MCHECK(pthread_mutex_destroy(&mutex_));
  }

  // must be called when locked, i.e. for assertion
  bool isLockedByThisThread() const
  {
    return holder_ == CurrentThread::tid();
  }

  void assertLocked() const ASSERT_CAPABILITY(this)
  {
    assert(isLockedByThisThread());
  }

  // internal usage

  void lock() ACQUIRE()
  {
    MCHECK(pthread_mutex_lock(&mutex_));
    assignHolder();
  }

  void unlock() RELEASE()
  {
    unassignHolder();
    MCHECK(pthread_mutex_unlock(&mutex_));
  }

  pthread_mutex_t* getPthreadMutex() /* non-const */
  {
    return &mutex_;
  }

 private:
  friend class Condition;

  class UnassignGuard : noncopyable
  {
   public:
    explicit UnassignGuard(MutexLock& owner)
      : owner_(owner)
    {
      owner_.unassignHolder();
    }

    ~UnassignGuard()
    {
      owner_.assignHolder();
    }

   private:
    MutexLock& owner_;
  };

  void unassignHolder()
  {
    holder_ = 0;
  }

  void assignHolder()
  {
    holder_ = CurrentThread::tid();
  }

  pthread_mutex_t mutex_;
  pid_t holder_;
};

// Use as a stack variable, eg.
// int Foo::size() const
// {
//   MutexLockGuard lock(mutex_);
//   return data_.size();
// }
class SCOPED_CAPABILITY MutexLockGuard : noncopyable
{
 public:
  explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
    : mutex_(mutex)
  {
    mutex_.lock();
  }

  ~MutexLockGuard() RELEASE()
  {
    mutex_.unlock();
  }

 private:

  MutexLock& mutex_;
};

}  // namespace muduo

// Prevent misuse like:
// MutexLockGuard(mutex_);
// A tempory object doesn't hold the lock for long!
#define MutexLockGuard(x) error "Missing guard object name"

#endif  // MUDUO_BASE_MUTEX_H

首先看Mutex的成员变量 pthread_mutex_t mutex_; pid_t holder_;
首先holder_是判断这个锁是不是在当前线程被使用。

MutexLock() 就是对mutex进行初始化的函数
isLockedByThisThread() 就是判断互斥量是否被当前线程所使用
lock() 加锁
unlock() 解锁
记得条件变量Condition为mutex的友元
MutexLockGuard是临界区
我们一般把MutexLockGuard定义在栈上,这样离开它的作用域,就自动调用析构函数
所以MutexLockGuard的构造函数为 对mutex_进行加锁,而析构函数为对mutex_进行解锁。

然后我们看Condition类的源代码,只需要看源文件.h

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#ifndef MUDUO_BASE_CONDITION_H
#define MUDUO_BASE_CONDITION_H

#include <muduo/base/Mutex.h>

#include <pthread.h>

namespace muduo
{

class Condition : 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_;
};

}  // namespace muduo

#endif  // MUDUO_BASE_CONDITION_H

explicit Condition(MutexLock& mutex):首先构造函数就是 对条件变量进行初始化
~Condition():对条件变量进行销毁
wait():wait函数还是比较重要的,我们要懂得pthread_cond_wait()的原理,首先它会把mutex_锁释放掉,然后在别的函数里,获得锁,然后对资源进行修改,然后此时在重新获得锁mutex_.  所以有一个释放锁和获得锁的过程。
notify()和notifyAll()的区别是:一个通知有资源可用,一个通知所有的线程。

最后一个CountDownLatch是一个重点讲的。
我觉得是对互斥量和条件变量的综合运用。

倒计时(CountDownLatch)它有2个应用:
1:主线程发起多个子进程,等这些子线程各自都完成一定的任务之后,主线程蔡继续执行。通常用于主线程等待多个子线程完成初始化。
2:主线程发起多个子线程,子线程都等待主线程,主线程完成其他一些任务之后通知所有子线程开始执行。通常用于多个子线程等待主线程发出“起跑”命令。

代码:

// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/base/CountDownLatch.h>

using namespace muduo;

CountDownLatch::CountDownLatch(int count)
  : mutex_(),
    condition_(mutex_),
    count_(count)
{
}

void CountDownLatch::wait()
{
  MutexLockGuard lock(mutex_);
  while (count_ > 0)
  {
    condition_.wait();
  }
}

void CountDownLatch::countDown()
{
  MutexLockGuard lock(mutex_);
  --count_;
  if (count_ == 0)
  {
    condition_.notifyAll();
  }
}

int CountDownLatch::getCount() const
{
  MutexLockGuard lock(mutex_);
  return count_;
}


// Use of this source code is governed by a BSD-style license
// that can be found in the License file.
//
// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include <muduo/base/CountDownLatch.h>

using namespace muduo;

CountDownLatch::CountDownLatch(int count)
  : mutex_(),
    condition_(mutex_),
    count_(count)
{
}

void CountDownLatch::wait()
{
  MutexLockGuard lock(mutex_);
  while (count_ > 0)
  {
    condition_.wait();
  }
}

void CountDownLatch::countDown()
{
  MutexLockGuard lock(mutex_);
  --count_;
  if (count_ == 0)
  {
    condition_.notifyAll();
  }
}

int CountDownLatch::getCount() const
{
  MutexLockGuard lock(mutex_);
  return count_;
}


然后我们看一个子线程等待主线程的例子:

代码:

#include<muduo/base/CountDownLatch.h>
#include<muduo/base/Thread.h>
#include<boost/bind.hpp>
#include<iostream>

using namespace muduo;

CountDownLatch latch_(1);

void Function()
{
    latch_.wait();
    printf("Thread ID = %d, Thread name = %s\n", CurrentThread::tid(), CurrentThread::name());
}

int main()
{
    Thread t1(boost::bind(Function),"Thread 1");
    Thread t2(boost::bind(Function),"Thread 2");
    t1.start();
    t2.start();
    
    printf("main Thread is running,before CountDown\n");
    latch_.countDown();
    sleep(3);
    printf("Sub Thread is running,after CountDown\n");

    t1.join();
    t2.join();
    return 0;
}

只有主线程发起命令,即latch_.countDown();子线程才会运行。
运行结果截图:

在这里插入图片描述
然后看一个主线程等待子线程的代码:

#include<muduo/base/CountDownLatch.h>
#include<muduo/base/Thread.h>
#include<muduo/base/Atomic.h>
#include<boost/bind.hpp>
#include<muduo/base/Mutex.h>
#include<iostream>
using namespace muduo;

muduo::AtomicInt32 cnt;

CountDownLatch latch_(2);

void Function()
{
    latch_.countDown();
    cnt.incrementAndGet();
    printf("Thread ID = %d, Thread name = %s\n", CurrentThread::tid(), CurrentThread::name());
}

int main()
{
    Thread t1(boost::bind(Function),"Thread 1");
    Thread t2(boost::bind(Function),"Thread 2");
    printf("before son child:%d\n", cnt.get());
    t1.start();
    t2.start();
    sleep(3);
    printf("main wait for son pthread running over.\n");
    latch_.wait();
    printf("son pthread running over\n");
    printf("After son child:%d\n", cnt.get());

    t1.join();
    t2.join();
    return 0;
}

这里是将latch_的值设置为2,那么只有当t1,t2线程执行完毕以后,才会调用主线程,不然一直阻塞在latch_.wait()上。

运行截图:
在这里插入图片描述
我们对cnt变量定义的是原子操作,所以经过两次递增,变为2.成功实现,主线程等待子线程

猜你喜欢

转载自blog.csdn.net/u014303647/article/details/88398641