使用RAII封装互斥锁和条件变量

最近在看陈硕的《Linux 多线程服务端编程:使用 muduo C++ 网络库》,这算是第一篇读书笔记吧。先介绍三个简单的工具类:MutexLock、MutexLockGuard 和 Condition:

  1. MutexLock 封装临界区(critical section),这是一个简单的资源类,用 RAII(Resource Acquisition Is Initialization)封装互斥锁的创建与销毁,一般作为别的 class 的数据成员。
  2. MutexLockGuard 封装临界区的进入和退出,即加锁和解锁。MutexLockGuard 一般是个栈上对象,它的作用域刚好等于临界区域。
  3. Condition 是对条件变量的简单封装,它有一个 MutexLock 数据成员。如果一个 class 要包含 MutexLock 和 Condition,请注意他们的声明顺序,MutexLock 数据成员应先于 Condition 构造,并作为后者的构造参数。
#include <boost/noncopyable.hpp>
#include <pthread.h>

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()
{ 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_;
};

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()); }

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

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

private:
MutexLock& mutex_;
pthread_cond_t pcond_;
};

上面三个工具类的最简单应用就是生产者和消费者问题,可以用它们实现 BlockingQueue:

#include <boost/noncopyable.hpp>
#include <deque>

template <typename T>
class BlockingQueue : boost::noncopyable
{
public:
BlockingQueue()
: mutex_(), notEmpty_(mutex_), queue_()
{ }

void put(T&& x)
{
MutexLockGuard lock(mutex_);
queue_.push_back(std::move(x));
notEmpty_.notify();
}

T take()
{
MutexLockGuard lock(mutex_);
while (queue_.empty()) {
notEmpty_.wait();
}

T front(std::move(queue_.front()));
queue_.pop_front();
return front;
}

size_t size() const
{
MutexLockGuard lock(mutex_);
return queue_.size();
}

private:
mutable MutexLock mutex_;
Condition notEmpty_;
std::deque<T> queue_;
};

关于上面的代码,有两点需要注意:

  1. 代码中使用 std::move 将对象转换为右值引用,如果类型 T 实现了 move 构造函数,这两处将会避免对象的 copy 构造,提高性能。
  2. 另外使用了 mutable 关键字,原因是 MutexLockGuard 的构造函数接受 non-const 的 MutexLock,为了在 const 成员函数中使用 mutex_ 成员构造 MutexLockGuard,需要使用 mutable 修饰以突破 const 的限制。
0%