muduo C++ 网络库——线程同步精要(2):条件变量

条件变量

条件变量的详细介绍在之前的一篇博文中:

Linux线程同步——条件变量


互斥器是加锁原语,用来排他性地访问共享数据,使用mutex时,我们希望立即拿到锁,然后尽快访问数据,用完之后尽快解锁,这样才能不影响并发性和性能。

先介绍moduo中condition的封装:

[cpp]  view plain  copy
  1. class Condition : boost::noncopyable  
  2. {  
  3.     public:  
  4.         explicit Condition(MutexLock& mutex) : mutex_(mutex)  
  5.         {   pthread_cond_init(&pcond_,NULL);    }  
  6.           
  7.         ~Condition() {  pthread_cond_destroy(&pcond_);  }  
  8.         void wait() {   pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());   }  
  9.         void notify()   {   pthread_cond_signal(&pcond_ );  }  
  10.         void notifyAll()    {   pthread_cond_broadcast(&pcond_);    }  
  11.   
  12.     private:  
  13.         MutexLock&  mutex_;  
  14.         pthread_cond_t  pcond_;  
  15. };  

如果需要等待某个条件成立,应该使用条件变量。条件变量的学名叫管程(monitor)。

对于wait端:

1.必须与mutex一起使用,该布尔表达式的读写需受此mutex保护

2.在mutex已上锁的时候才能调用wait()

3.把判断布尔条件和wait()放到while循环中

代码: 

[cpp]  view plain  copy
  1. muduo::MutexLock mutex;  
  2. muduo::Condition cond(mutex);  
  3. std::deque<int> queue;  
  4.   
  5. int dequeue()  
  6. {  
  7.     MutexLockGuard lock(mutex);  
  8.     while(queue.empty())                //必须用循环;必须在判断之后再wait()  
  9.     {  
  10.         cont.wait();        //这一步会原子地unlock mutex并进入等待。不会与enqueue死锁;wait()执行完毕后会自动重新加锁  
  11.     }  
  12.     assert(!queue.empty());             //判断条件是否变化  
  13.     int top = queue.front();  
  14.     queue.pop_front();  
  15. }  
  16.   
  17. void enqueue(int x)  
  18. {  
  19.     MutexLockGuard lock(mutex);  
  20.     queue.push_back(x);  
  21.     cond.notify();          //muduo:Condition采用了notify()和notifyAll()为函数名,避免重载signal这个术语(如上)  
  22. }  

上面代码中必须用while循环来等待条件变量,而不用if语句。原因是spurious wakeup。

 

条件变量是非常底层的同步原语,很少直接使用,一般用它来实现高层的同步措施,如CountDownLatch。

倒计时(CountDownLatch)是一种常用且易用的同步手段。主要有两种用途:

1.主线程发起多个子线程,等这些子线程各自都完成一定的任务之后,主线程才继续执行。通常用于主线程等待多个子线程完成初始化。

2.主线程发起多个子线程,子线程都等待主线程,主线程完成其他一些任务之后通知所有子线程开始执行。通常用于多个子线程等待主线程发出“起跑”命令。

CountDownLatch接口:

[cpp]  view plain  copy
  1. class CountDownLatch : boost::nonqcopyable  
  2. {  
  3.     public:  
  4.         explicit CountDownLatch(int count);     //倒数几次  
  5.         void wait();                            //等待计数值变为0  
  6.         void countDown();                       //计数减1  
  7.           
  8.     private:  
  9.         mutable Mutexlock mutex_;  
  10.         Condition condition_;  
  11.         int count_;  
  12. };  

CountDownLatch实现:

[cpp]  view plain  copy
  1. /* 构造函数:mutex_应先于condition_构造 */  
  2. class CountDownLatch  
  3. {  
  4.     public:  
  5.         CountDownLatch(int count): mutex_(),condition_(mutex_),count_(count) {}  
  6.     private:  
  7.         mutable MutexLock mutex_;       //顺序很重要,先mutex后condition  
  8.         Condition condition_;  
  9.         int count_:  
  10. }  
  11.   
  12. void CountDownLatch::wait()  
  13. {  
  14.     MutexLockGuard  lock(mutex_);  
  15.     while(count_ > 0)  
  16.         condition_.wait();  
  17. }  
  18.   
  19. void CountDownLatch::countDown()  
  20. {  
  21.     MutexLockGuard  lock(mutex_);  
  22.     --count_;  
  23.     if (count == 0)  
  24.         condition_.notyfiAll();  
  25. }  

互斥器和条件变量构成了多线程编程的全部必备同步原语,用它们即可完成任何多线程同步任务,二者不能相互替代。

猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/80750826