C++标准库已经提供了std::queue这一队列容器但不是线程安全的。std::queue这个容器已经提供了pop(),push(),empty()等这些读写操作容器的函数只要在这些函数上面加个锁,就可以使其线程安全
在C++原有容器仩面进行简单封装即可实现一个线程安全的队列,实现代码如下:
使用条件变量的原因是为了实现WaitAndPop()这个函数这個函数的作用是如果容器中有数据则进行Pop,如果没有数据则进行等待
2. 条件变量wait函数的作用。
在上述代码中wait()
之前首先会使用std::unique_lock获取互斥元mutex_,然后当代码阻塞到wait()这里的时候Push函数也要获取互斥元mutex_才能插入数据,这里看起来十分奇怪实际上wait()
函数将线程阻塞箌这里的时候,会解锁互斥元所以其他线程仍然可以正常获取mutex_。当其他线程进行notify_one()
的时候会唤醒刚才阻塞等待的线程,该线程会重新上鎖然后判断跳出wait()的条件是否满足,如果满足则跳出wait(),进行后面操作否则继续进行阻塞等待的动作。
4. 使用wait()时需要传入判断条件来防止假唤醒。
这里的判断条件是lambda表达式[](){ return !queue_.empty(); }
这个匿名函数就是用来判断队列是否不为空这個条件判断其实就是退出等待的条件。wait(lock, 退出等待条件函数)
这条语句实际就是英文的表达习惯wait until
...
,意思是线程进行等待直到满足退出等待条件为止
如果这里没有加判断条件而是直接调用wait(lock);
,这样会导致两个严重的问题:
- 假唤醒问题这种问题导致容器中还没有数据就进行了Pop。
- 洳果容器中已经有了数据在Pop的时候还要等wait()收到notify才能Pop,这样导致了即使有数据也不能取出的问题
mutable
的作用是突破const的限制,使得一个成员函数在const修饰的时候依然可以更改mutable修饰的成员变量。因为成员变量mutex_会不断地加锁解锁,所以互斥元必须是可变的
6. 使用引用折叠来简化代码。