在上一篇博客學習了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