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