条件遍历是一种同步机制,它阻塞线程直到满足某个条件,避免忙等待。C++11提供了两种条件变量,需要添加头文件#include<condition_variable>
- condition_variable:配合std::unique_lock<std::mutex>进行wait操作
- condition_variable_any: 和任意带有lock、unlock语义的mutex搭配使用,比较灵活,但效率比condition_variable差一些
- 可以用condition_variable_any实现一个同步队列,它是一个简单的生产者/消费者模型的实现,缺点是会产生饥饿。
#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
#include<condition_variable>
#include<list>
template<typename T>
class SyncQueue {
public:
bool empty() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
bool full() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size == m_maxSize();
}
SyncQueue(int maxSize):m_maxSize(maxSize){
}
void put(const T& x) {
std::lock_guard<std::mutex> locker(m_mutex);
// while(isFull()){
// std::cout << "the buffer is full" << std::endl;
// m_notFull.wait(m_mutex); //等待临界区未满的条件
// }
m_notFull.wait(m_mutex, [this]{return !isFull();});
m_queue.push_back(x);
m_notEmpty.notify_one();
}
void Take(T& x) {
std::lock_guard<std::mutex> locker(m_mutex);
// while(isEmpty()) {
// std::cout << "the buffer is empty" << std::endl;
// m_notEmpty(m_mutex);//等待临界区非空的条件
// }
m_notEmpty.wait(m_mutex, [this]{return !isEmpty();}); //这种写法与上面的等价,但更简洁
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();// 唤醒一个在等待临界区未满的线程
}
size_t Size() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
int Count() {
return m_queue.size();
}
private:
bool isFull() const {//这是一个非同步方法
return m_queue.size() == m_maxSize;
}
bool isEmpty() const {//这是一个非同步方法
return m_queue.empty();
}
std::list<T> m_queue;
std::mutex m_mutex;
std::condition_variable_any m_notEmpty;
std::condition_variable_any m_notFull;
int m_maxSize;
};
上面代码中wait会释放mutex,而lock_guard此时还拥有mutex,出现了语义不一致。如果此时线程结束,lock_guard析构会导致错误。
下面用condition_variable 和 unique_lock来实现同步队列。unique_lock可以随时释放锁(lock_guard析构的时候才会释放),因此wait函数中释放锁不会导致语义不一致
#include<iostream>
#include<thread>
#include<mutex>
#include<chrono>
#include<condition_variable>
#include<list>
template<typename T>
class SyncQueue {
public:
bool empty() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.empty();
}
bool full() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size == m_maxSize();
}
SyncQueue(int maxSize):m_maxSize(maxSize){
}
void put(const T& x) {
std::unique_lock<std::mutex> locker(m_mutex);
m_notFull.wait(m_mutex, [this]{return !isFull();});
m_queue.push_back(x);
m_notEmpty.notify_one();
}
void Take(T& x) {
std::unique_lock<std::mutex> locker(m_mutex);
m_notEmpty.wait(m_mutex, [this]{return !isEmpty();}); //这种写法与上面的等价,但更简洁
x = m_queue.front();
m_queue.pop_front();
m_notFull.notify_one();// 唤醒一个在等待临界区未满的线程
}
size_t Size() {
std::lock_guard<std::mutex> locker(m_mutex);
return m_queue.size();
}
int Count() {
return m_queue.size();
}
private:
bool isFull() const {//这是一个非同步方法
return m_queue.size() == m_maxSize;
}
bool isEmpty() const {//这是一个非同步方法
return m_queue.empty();
}
std::list<T> m_queue;
std::mutex m_mutex;
std::condition_variable m_notEmpty;
std::condition_variable m_notFull;
int m_maxSize;
};
网友评论