1. MutexLock
封装了linux底层的pthread_mutex_init、pthread_mutex_destroy、pthread_mutex_lock和pthread_mutex_unlock等函数。
class CAPABILITY("mutex") MutexLock : 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_CAPABILITY(this)
{
assert(isLockedByThisThread());
}
// internal usage
void lock() ACQUIRE() //仅供MutexLockGuard调用,严禁用户代码调用
{
MCHECK(pthread_mutex_lock(&mutex_)); //这两行顺序不能反
assignHolder();
}
void unlock() RELEASE() //仅供MutexLockGuard调用,严禁用户代码调用
{
unassignHolder(); //这两行顺序不能反
MCHECK(pthread_mutex_unlock(&mutex_));
}
pthread_mutex_t* getPthreadMutex() /* non-const */
{
return &mutex_;
}
private:
friend class Condition;
class UnassignGuard : noncopyable
{
public:
explicit 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_;
};
2. MutexLockGuard
class SCOPED_CAPABILITY MutexLockGuard : noncopyable
{
public:
explicit MutexLockGuard(MutexLock& mutex) ACQUIRE(mutex)
: mutex_(mutex)
{
mutex_.lock();
}
~MutexLockGuard() RELEASE()
{
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
注意一个宏定义:
#define MutexLockGuard(x) error "Missing guard object name"
主要是为了防止程序中出现以下错误:
void doit()
{
MutexLockGuard(mutex); //遗漏变量名,产生一个临时对象马上被销毁了,结果没有锁住临界区
//正确的写法 MutexLockGuard lock(mutex)
//临界区
}
3. Condition
封装了linux底层的pthread_cond_init、pthread_cond_destroy、pthread_cond_wait、pthread_cond_signal和pthread_cond_broadcast等,下面也将会介绍下这几个函数。
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_;
};
如果一个class要包含MutexLock和Condition,请注意他们的声明顺序和初始化顺序,mutex_应先于conditon_构造,并作为后者的构造函数:
class CountDownLatch
{
public:
CountDownLatch(int count)
: mutex_(),
condition_(mutex_),
count_(count)
{
}
private:
mutable MutexLock mutex_;
Condition condition_ GUARDED_BY(mutex_);
int count_ GUARDED_BY(mutex_);
}
4. pthread_cond_signal、pthread_cond_broadcast和pthread_cond_wait
4.1 pthread_cond_signal
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);
作用:发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行.如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。
使用pthread_cond_signal一般不会有“惊群现象”产生,他最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。但无论如何一个pthread_cond_signal调用最多发信一次。
但是 pthread_cond_signal 在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求 pthread_cond_signal 至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程。--------------造成"虚假唤醒"。
4.2 pthread_cond_broadcast
#include <pthread.h>
int pthread_cond_broadcast(pthread_cond_t *cond);
作用:唤醒所有线程。
4.3 问题引入
我们有两个线程:线程1和线程2,他们用于处理同一个共享数据,处理数据时先获取mutex,再释放mutex,过程如下:
1)线程1获取mutex,在进行数据处理的时候,线程2也想获取mutex,但是此时被线程1所占用,线程2进入休眠,等待mutex被释放;
2)线程1做完数据处理后,调用pthread_cond_signal()唤醒等待队列中某个线程,在本例中也就是线程2。线程1在调用pthread_mutex_unlock()前,因为系统调度的原因,线程2获取使用CPU的权利,那么它就想要开始处理数据,但是在开始处理之前,mutex必须被获取,很遗憾,线程1正在使用mutex,所以线程2被迫再次进入休眠;
3)然后就是线程1执行pthread_mutex_unlock()后,线程2方能被再次唤醒;
由此可见,这种执行效率是非常低的,尤其是在多线程的环境中。
4.4 pthread_cond_wait
#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
注意:一定要在mutex的锁定区域内使用。
作用:pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用 pthread_cond_signal() 或 pthread_cond_broadcast来唤醒它 。pthread_cond_wait() 必须与pthread_mutex 配套使用。 pthread_cond_wait() 函数一进入wait状态就会自动release mutex。当其他线程通过 pthread_cond_signal() 或 pthread_cond_broadcast ,把该线程唤醒,使 pthread_cond_wait()通过(返回)时,该线程又自动获得该 mutex 。
例如:
int dequeue()
{
MutexLockGuard lock(mutex);
while(queue.empty()) //必须用循环;必须在判断之后再wait()
{
cond.wait(); //这一步原子地unlock mutex并进入等待,不会与enqueue死锁
//wait()执行完毕时会自动重新加锁
}
assert(!queue.empty());
int top = queue.front();
queue.pop_front();
return top;
}