线程同步的四项准则
- 最低限度的共享对象,缩小须要同步的场合
- 应用高级的并发构件,如 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