互斥器mutex用来排他性的访问共享数据,他不是等待原语。若要等待某个条件成立,我们应该使用条件变量(condition variable)。条件变量顾名思义就是一个或多个线程等待某个布尔表达式为真,即等待别的线程唤醒它。
条件变量只有一种使用方式,wait端:
1、必须与Mutex一起使用,以保护布尔表达式的读写收到保护。
2、Mutex上锁后才能调用wait()等待。
3、把wait()放到 while循环内,而不是if语句块内。否则会导致假唤醒。
对于条件变量,我的简单理解就是消费者和生产者:notify一段为生产者,告诉wait端有元素可以消费了。我在项目中最长遇到的就是将其用到BlockingQueue中。下面是简单的实现:
生产者, 将元素去出队列:
1 void enqueue(T t) 2 { 3 std::lock_guard<std::mutex> lock(m); 4 q.push_back(t); 5 c.notify_one(); 6 }
消费者,将T value放入队列:
1 T dequeue(T &value) 2 { 3 std::unique_lock<std::mutex> lock(m); 4 while (q.empty()) { //必须使用循环,必须在判断后再wait() 5 c.wait(); 6 } 7 value = q.front(); 8 q.pop(); 9 return value; 10 }
第五行代码c.wait()会把线程投入睡眠,并释放线程持有的互斥锁,当wait()返回时,该再次上锁,该线程再次持有该互斥锁。(这个怎么做到的?释放互斥锁,然后睡眠期间,该锁不会被别的线程持有吗?)
注意:一定要使用循环判断queue是否有元素可用,才调能用c.wait()。不然可能产生假唤醒。因为notify_one(调用的是pthread_cond_signal)或者notify_all(调用的是pthread_cond_broadcast)都只能唤醒正在wait()的线程,这就是所谓的边沿触发。
注:本文参考http://www.cppblog.com/Solstice/archive/2015/10/30/203094.html