线程同步的四项准则
- 最低限度的共享对象,缩小须要同步的场合
- 应用高级的并发构件,如TaskQueue,Producer-Consumer Queue,CountDownLatch等
- 非不得已应用底层同步原语时,只应用非递归的互斥器和条件变量,慎用读写锁,不要应用信号量
- 除了应用atomic整数外,不本人编写lock-free代码,也不要应用内核级同步原语
互斥器(mutex)
应用准则
-
应用RAII手法封装mutex的创立、销毁、加锁、解锁
- 保障了锁的失效期间等于一个作用域(Scoped Locking),在死锁产生时有助于定位
- 保障了不手工调用lock和unlock
- 保障了不跨过程和线程调用锁
- 保障了不遗记解锁和反复加锁
- 只应用非递归的mutex(即不可重入的mutex)
- 每次结构Guard对象时,思考一路上曾经持有的锁(函数调用栈),避免因桎梏程序不同导致的死锁
为什么只应用非递归的mutex
- 同一个线程能够反复对递归的mutex加锁,但不能反复对非递归的mutex加锁
- 在同一个县城里屡次对非递归的mutex加锁会导致死锁
- 两者性能差异不大
起因
- 非递归的锁能提前暴露出编程问题
死锁
- 除了线程间的死锁,单个线程也会造成死锁
- 在保障应用Scoped Locking时,能够通过剖析调用栈失去起因
条件变量
应用条件变量实现的BlockingQueue
muduo::MutexLock mutex;
muduo::Condition cond(mutex);
std::deque<int> queue;
int dequeue(){
MutexLockGuard lock(mutex);
while(queue.empty()){
cond.wait(); // unlock mutex and wait (原子地)
}
assert(!queue.empty());
int top = queue.front();
queue.pop_front();
return top;
}
void enqueue(int x){
{
MutexLockGuard lock(mutex);
queue.push_back(x);
}
cond.notify(); // wakeup the wait side
}
- 每次退出都调用notify(),而不是notifyall()是为了防止惊群效应
- 不只在0->1的时候notify(),而是每加一次告诉一次,是当存在多个消费者时,能高效地唤醒他们,不然只唤醒了一个
- 详见 https://book.douban.com/annot…
应用条件变量实现CountDownLatch(倒计时)
class CountDownLatch : boost::noncopyable{
public:
explicit CountDownLatch(int count);
void wait();
void countDown();
private:
mutable MutexLock mutex_;
Condition condition_;
int count_;
}
void CountDownLatch::wait(){
MutexLockGuard lock(mutex_);
whilt(count_>0){
condition_.wait();
}
}
void CountDownLatch::countDown(){
MutexLockGuard lock(mutex_);
--count_;
if(count_ == 0){
condition_.notifyAll();
}
}
封装MutexLock、MutexLockGuard、Condition的实现
class MutexLock : boost::noncopyable{
public:
MutexLock()
:holder_(0)
{
pthread_mutex_init(&mutex_NULL);
}
~MutexLock()
{
assert(hoilder_ == 0);
pthread_mutex_destory(&mutex);
}
bool isLockedByThisThread(){
return holder_ == CurrentThread::tid();
}
void assertLocked(){
assert(isLockedByThisThread());
}
void lock(){
pthread_mutex_lock(&mutex);
holder_ = CurrentThread::tid();
}
void unlock(){
holder_ = 0;
pthread_mutex_unlock();
}
pthread_mutex_t* getPthreadMutex(){
return &mutex;
}
private:
pthread_mutex_t mutex_;
pid_t holder_;
}
class MutexLockGuard : boost::noncopyable{
public:
explicit MutexLockGuard(MutexLock& mutex)
:mutex_(mutex)
{
mutex_.lock();
}
~MutexLockGuard(){
mutex_.unlock;
}
private:
MutexLock& mutex_;
}
#define MutexLockGuard(x) static_assert(false,"Missing mutex guard variable name!")
class Condition : boost::noncopyable{
public:
explicit Condition(MutexLock& mutex)
:mutex_(mutex)
{
pthread_cond_init(&pcond_,NULL);
}
~Condition(){
pthread_cond_destory(&pcond_);
}
void wait(){
pthread_cond_wait(&pcond_,mutex_.getPthreadMutex());
}
void notify(){
pthread_cond_signal(&pcond_);
}
void notifyAll(){
pthread_cond_boardcast(&pcond);
}
private:
MutexLock& mutex_;
pthread_cond_t pcond_;
}
小结
- 线程同步的四项准则:尽量应用高级同步设施
- 对于其余的工作,应用一般互斥器和条件变量,采纳RAII手法和Scooped Locking
发表回复