muduo库MutexLock类 MutexLockGuard类 Condition类 CountDownLatch类封装 ptr_vector小知识

MutexLock类

DartNet

MutexLock类的实现

class MutexLock : boost::noncopyable
{
 public:
  MutexLock()
    : holder_(0)
  {
    int ret = pthread_mutex_init(&mutex_, NULL);
    assert(ret == 0); (void) ret;
  }

  ~MutexLock()
  {
    assert(holder_ == 0);
    int ret = pthread_mutex_destroy(&mutex_);
    assert(ret == 0); (void) ret;
  }

  bool isLockedByThisThread()
  {
    return holder_ == CurrentThread::tid();
  }

  void assertLocked()
  {
    assert(isLockedByThisThread());
  }

  // internal usage

  void lock()
  {
    pthread_mutex_lock(&mutex_);
    holder_ = CurrentThread::tid();
  }

  void unlock()
  {
    holder_ = 0;
    pthread_mutex_unlock(&mutex_);
  }

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

 private:

  pthread_mutex_t mutex_;
  pid_t holder_;
};

MutexLockGuard类

DartNet

MutexLockGuard类的实现

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

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

 private:

  MutexLock& mutex_;  // 此处是引用,MutexLockGuard析构时,mutex_的生存期并没有结束
};

MutexLockGuard类使用RAII技法封装,在对象构造时获取资源,在对象析构的时候释放资源。我们把管理一份资源的责任托管给了一个对象。平时更常用MutexLockGuard,作用域内加锁,可以防止忘记解锁。

Condition类

DartNet

Condition类的实现

class Condition : boost::noncopyable
{
 public:
  explicit Condition(MutexLock& mutex)
    : mutex_(mutex)
  {
    pthread_cond_init(&pcond_, NULL);
  }

  ~Condition()
  {
    pthread_cond_destroy(&pcond_);
  }

  void wait()
  {
    pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());
  }

  // returns true if time out, false otherwise.
  bool waitForSeconds(int seconds);

  void notify()
  {
    pthread_cond_signal(&pcond_);
  }

  void notifyAll()
  {
    pthread_cond_broadcast(&pcond_);
  }

 private:
  MutexLock& mutex_;
  pthread_cond_t pcond_;
};

条件变量与互斥锁的通常情况下配合使用

explicit Condition(MutexLock& mutex)
    : mutex_(mutex)
{
    pthread_cond_init(&pcond_, NULL);
}

条件变量的正确使用方式

某个线程:
加锁     // 加锁,防止条件被其他线程更改                               
     while (条件)
          wait();  
解锁

另一个线程:
加锁
     更改条件
     通知notify(可以移到锁外)
解锁
pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());

等待线程的三个步骤:

  1. 解锁;如果没有解锁,其他线程,没法进入临界区,修改条件,条件将始终没法成立。
  2. 等待通知;
  3. 得到通知返回前重新加锁; 重新加锁,是下面的解锁操作对应起来

CountDownLatch类

DartNet

CountDownLatch类既可以用于所有子线程等待主线程发起起跑,也可以用于主线程等待子线程初始化完毕才开始工作。

CountDownLatch类的部分实现

explicit CountDownLatch(int count);

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

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

主线程发起起跑

#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>

#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <string>
#include <stdio.h>

using namespace muduo;

class Test
{
public:
    Test(int numThreads)
        : latch_(1),
          threads_(numThreads)
    {
        for (int i = 0; i < numThreads; ++i)
        {
            char name[32];
            snprintf(name, sizeof name, "work thread %d", i);
            threads_.push_back(new muduo::Thread(
                                   boost::bind(&Test::threadFunc, this), muduo::string(name)));
        }
        for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::start, _1));
    }

    void run()
    {
        latch_.countDown();
    }

    void joinAll()
    {
        for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::join, _1));
    }

private:

    void threadFunc()
    {
        latch_.wait();
        printf("tid=%d, %s started\n",
               CurrentThread::tid(),
               CurrentThread::name());



        printf("tid=%d, %s stopped\n",
               CurrentThread::tid(),
               CurrentThread::name());
    }

    CountDownLatch latch_;
    boost::ptr_vector<Thread> threads_;
};

int main()
{
    printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid());
    Test t(3);
    sleep(3);
    printf("pid=%d, tid=%d %s running ...\n", ::getpid(), CurrentThread::tid(), CurrentThread::name());
    t.run();
    t.joinAll();

    printf("number of created threads %d\n", Thread::numCreated());
}

程序输出

```

可以看到其他三个线程一直等到主线程睡眠完执行run(),在里面执行latch_.countDown()将计数减为0,进而执行notifyall唤醒后,才开始执行下来。

#### 主线程等待子线程
```C++
#include <muduo/base/CountDownLatch.h>
#include <muduo/base/Thread.h>

#include <boost/bind.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <string>
#include <stdio.h>

using namespace muduo;

class Test
{
public:
    Test(int numThreads)
        : latch_(numThreads),
          threads_(numThreads)
    {
        for (int i = 0; i < numThreads; ++i)
        {
            char name[32];
            snprintf(name, sizeof name, "work thread %d", i);
            threads_.push_back(new muduo::Thread(
                                   boost::bind(&Test::threadFunc, this), muduo::string(name)));
        }
        for_each(threads_.begin(), threads_.end(), boost::bind(&muduo::Thread::start, _1));
    }

    void wait()
    {
        latch_.wait();
    }

    void joinAll()
    {
        for_each(threads_.begin(), threads_.end(), boost::bind(&Thread::join, _1));
    }

private:

    void threadFunc()
    {
        sleep(3);
       printf("tid=%d, %s started\n",
               CurrentThread::tid(),
               CurrentThread::name());

        latch_.countDown();


        printf("tid=%d, %s stopped\n",
               CurrentThread::tid(),
               CurrentThread::name());
    }

    CountDownLatch latch_;
    boost::ptr_vector<Thread> threads_;
};

int main()
{
    printf("pid=%d, tid=%d\n", ::getpid(), CurrentThread::tid());
    Test t(3);
    t.wait();
    printf("pid=%d, tid=%d %s running ...\n", ::getpid(), CurrentThread::tid(), CurrentThread::name());
    t.joinAll();

    printf("number of created threads %d\n", Thread::numCreated());
}

程序输出

可以看出当其他三个线程都启动后,各自执行一次latch_.countDown(),主线程wait() 返回继续执行下去。

ptr_vector小知识

boost::ptr_vector专门用于动态分配的对象,它使用起来更容易也更高效。容器在析构的时候,会自动清理指针