Mudoo网络库中使用RAII的方式封装了同步原语,自己实现了互斥锁和条件变量,这里记录一下方便自己理解。
其中最主要的几个类有:MutexLock、MutexLockGuard、Condition以及CountDownLatch。其中MutexLock和Condtion是对于posix线程库中的互斥锁和条件变量的封装,MutexLockGuard和CountDownLatch则是对他们的封装。
MutexLock类的源码如下:
class MutexLock : boost::noncopyable
{
public:
MutexLock()
: holder_(0)
{
MCHECK(pthread_mutex_init(&mutex_, NULL));
}
~MutexLock()
{
assert(holder_ == 0);
MCHECK(pthread_mutex_destroy(&mutex_));
}
// must be called when locked, i.e. for assertion
bool isLockedByThisThread() const
{
return holder_ == CurrentThread::tid();
}
void assertLocked() const
{
assert(isLockedByThisThread());
}
// internal usage
void lock()
{
MCHECK(pthread_mutex_lock(&mutex_));
assignHolder();
}
void unlock()
{
unassignHolder();
MCHECK(pthread_mutex_unlock(&mutex_));
}
pthread_mutex_t* getPthreadMutex() /* non-const */
{
return &mutex_;
}
private:
friend class Condition;
class UnassignGuard : boost::noncopyable
{
public:
UnassignGuard(MutexLock& owner)
: owner_(owner)
{
owner_.unassignHolder();
}
~UnassignGuard()
{
owner_.assignHolder();
}
private:
MutexLock& owner_;
};
void unassignHolder()
{
holder_ = 0;
}
void assignHolder()
{
holder_ = CurrentThread::tid();
}
pthread_mutex_t mutex_;
pid_t holder_;
};
构造函数:
MutexLock()
: holder_(0)
{
MCHECK(pthread_mutex_init(&mutex_, NULL));
}
这个类有两个数据成员mutex_为posix线程库的互斥锁变量, holder_表示当前锁的持有者,构造函数不带参数,一个Mutex类被初始化时先将互斥锁初始化为默认的不可重入型,所有者holder_为0,表示没有所有者持有。MCHEK是google-glog的一个宏,用于检查返回值。因为assert在release版本中并没有效果。
加锁函数:
void lock()
{
MCHECK(pthread_mutex_lock(&mutex_));
assignHolder();
}
加锁函数将该类所拥有的互斥锁加锁,然后将所有者的线程号赋值为当前线程号。注意这个函数的加锁和给holder_赋值的顺序不能改变,只有已经获得了互斥锁才能确定当前线程为所有者。这个函数将在MutexLockGuard的构造函数中被调用。
解锁函数:
void unlock()
{
unassignHolder();
MCHECK(pthread_mutex_unlock(&mutex_));
}
该函数先将所有者holder_设置为空然后再解锁,注意这里的顺序依然不可以改变。这个函数将在MutexLockGuard的析构函数中被调用。
下面是MutexLockGuard类
class MutexLockGuard : boost::noncopyable
{
public:
explicit MutexLockGuard(MutexLock& mutex)
: mutex_(mutex)
{
mutex_.lock();
}
~MutexLockGuard()
{
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
MutexLockGuard类的私有数据成员为一个MutexLock的引用,在其构造函数中通过初始化列表初始化该引用后,调用mutex_的成员函数lock()进行加锁,在析构函数中调用unlock()对其进行解锁。MutexLockGuard应为一个栈上对象,在生命周期结束时调用析构函数自动解锁。一个简单的用法如下:
MutexLock mutex_;
.
.
.
//语句块
{
MutexLockGuard lock(mutex);
.
.//受保护的语句
.
}
lock的生命周期在大括号内,生命周期结束后会自动析构,析构函数调用MutexLock的unlock函数自动解锁,因此不需要在手动的加锁和解锁,减少了出错的概率。
condition类的源代码如下
class Condition : boost::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_;
该类即对于条件变量的封装,该类的数据成员为一个MutexLock类和一个条件变量,mudoo的作者陈硕老师认为一个条件变量正常情况下是不会和不同的互斥锁配合使用,因此这里以一个条件变量只能对应一个互斥锁(当然,一个互斥锁是可以对应多个条件变量的,例如使用条件变量解决生产者消费者问题的时候就经常如此)。
该类的构造函数接受一个MutexLock类的引用作为参数初始化mutex_,并且初始化条件变量。而析构函数则释放该条件变量。
wait()函数就是条件变量的,阻塞并等待对应的通知。先取消持有者的线程号然后调用pthread_cond_wait阻塞本线程并等待通知。
notify()和notifyall()分别封装了发信号通知某一给等待的条件变量和广播通知所有的条件变量。