Pthread 是标准库提供的 API,实际使用是 tough 的,下面看看实际上,该如何封装 Pthread。
以下代码删减自Muduo,去除了错误检查相关,只保留核心思想。
一、Mutex
class MutexLock : boost::noncopyable
{
public:
MutexLock(){
pthread_mutex_init(&mutex_, NULL);
}
~MutexLock(){
pthread_mutex_destroy(&mutex_);
}
void lock(){
pthread_mutex_lock(&mutex_);
}
void unlock(){
pthread_mutex_unlock(&mutex_);
}
pthread_mutex_t* getPthreadMutex() /* non-const */{
return &mutex_;
}
private:
pthread_mutex_t mutex_;
};
class MutexLockGuard : boost::noncopyable
{
public:
explicit MutexLockGuard(MutexLock& mutex): mutex_(mutex){
mutex_.lock();
}
~MutexLockGuard(){
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
代码一看便知,不做过多讲解,通过RAII手法来控制 mutex 的生命周期,再通过 Guard 包装来保证 lock 和 unlock 的对应。
具体使用如下:
class Foo
{
public:
int size() const;
private:
mutable MutexLock mutex_;
std::vector<int> data_; // GUARDED BY mutex_
};
int Foo::size() const {
MutexLockGuard lock(mutex_);
return data_.size();
}
需要注意的是别写成了 MutexLockGuard(mutex_),这样产生的是一个匿名对象,该条语句结束后立即析构,没有锁住临界区。
二、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(double seconds);
void notify(){
pthread_cond_signal(&pcond_);
}
void notifyAll(){
pthread_cond_broadcast(&pcond_);
}
private:
MutexLock& mutex_;
pthread_cond_t pcond_;
};
与Mutex的设计是非常一致的,具体如何使用呢?请看下面的代码:
// 使用 Condition 实现一个等待计数到0的抽象
class CountDownLatch : boost::noncopyable
{
public:
explicit CountDownLatch(int count);
void wait();
void countDown();
int getCount() const;
private:
mutable MutexLock mutex_;
Condition condition_;
int count_;
};
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_;
}
需要注意的是,mutex_和conditin_的顺序很重要,需要先初始化 mutex_。
三、Thread
Thread 的抽象是利用上面的包裹和API进行包装,形成易于使用的接口,限于篇幅,就不贴出代码了,可见于:Thread.h
有几个注意的地方:
- Thread对象的析构并没有销毁线程句柄,实际上如果没有被join,就detach线程,避免资源泄漏
- 通过 CoundDownLatch,包装Thread::start()在线程真正被调度到后才返回。