在上一篇博客学习了muduo库对线程的封装,并且留了一个小尾巴,在muduo的线程类里面有一个成员变量是CountDownLatch类型,打开这个类可以看到这个类主要是对条件变量的封装。所以先学习muduo库对条件变量的封装。
conditon.h
#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
condition.cc
#include "muduo/base/Condition.h"
#include <errno.h>
// returns true if time out, false otherwise.
bool muduo::Condition::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);
}
可以看到conditon的封装并没有什么特殊的地方, 有两个成员变量,mutex和condition,mutex是一个引用类型,必须通过初始化列表初始,这也说明mutex不属于condtion类,这两者是关联的关系,在构造函数初始化conditon,析构函数销毁。
接下来我们看看CountDownLatch类是干什么的
CountDownLatch.h
#ifndef MUDUO_BASE_COUNTDOWNLATCH_H
#define MUDUO_BASE_COUNTDOWNLATCH_H
#include "muduo/base/Condition.h"
#include "muduo/base/Mutex.h"
namespace muduo
{
class CountDownLatch : noncopyable
{
public:
explicit CountDownLatch(int count);
void wait();
void countDown();
int getCount() const;
private:
mutable MutexLock mutex_;
Condition condition_ GUARDED_BY(mutex_);
int count_ GUARDED_BY(mutex_);
};
} // namespace muduo
#endif // MUDUO_BASE_COUNTDOWNLATCH_H
CountDownLatch.cc
// 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_;
}
可以看到,CountDownLatch主要是通过条件变量来实现,通过对条件变量和一个初始值大于0的整数组合,来出发被阻塞的线程。countDown的调用初始值会被–,知道初始值==0,触发唤醒函数。有点类似于信号量的一半,如果这边初始值可++,那么就可以通过条件变量和互斥锁实现一个信号量。
需要注意的点:
- mutable 类型变量,mutable可以修饰的成员变量不受const函数影响,在函数内依然可以修改状态。
作用:
-
既可以用于所有子线程等待主线程发起 “起跑” ,主线程调用countDown
-
也可以用于主线程等待子线程初始化完毕才开始工作,子线程调用countDown