使用现代化C++实现线程安全的阻塞队列
完整代码
template<typename T>
class BlockingQueue {
const size_t capacity_;
std::queue<T> queue_;
std::mutex mutex_;
std::condition_variable cvConsumer;
std::condition_variable cvProducer;
public:
BlockingQueue() : BlockingQueue(std::numeric_limits<size_t>::max()) {}
explicit BlockingQueue(size_t maxCapacity)
: capacity_(maxCapacity) {
assert(maxCapacity > 0);
}
[[nodiscard]]
T front() {
std::unique_lock<std::mutex> lock(mutex_);
cvConsumer.wait(lock, [this] {
return !queue_.empty();
});
return queue_.front();
}
[[nodiscard]]
T back() {
std::unique_lock<std::mutex> lock(mutex_);
cvConsumer.wait(lock, [this] {
return !queue_.empty();
});
return queue_.back();
}
void push(const T &e) {
std::unique_lock<std::mutex> lock(mutex_);
cvProducer.wait(lock, [this] {
return queue_.size() < capacity_;
});
queue_.emplace(e);
cvConsumer.notify_one();
}
void push(T &&e) {
std::unique_lock<std::mutex> lock(mutex_);
cvProducer.wait(lock, [this] {
return queue_.size() < capacity_;
});
queue_.emplace(std::forward<T>(e));
cvConsumer.notify_one();
}
T pop() {
std::unique_lock<std::mutex> lock(mutex_);
cvConsumer.wait(lock, [this] {
return !queue_.empty();
});
T value = std::move(queue_.front());
queue_.pop();
cvProducer.notify_one();
return value;
}
[[nodiscard]]
bool empty() {
std::lock_guard<std::mutex> lock(mutex_);
return queue_.empty();
}
[[nodiscard]]
size_t size() {
std::lock_guard<std::mutex> lock(mutex_);
return queue_.size();
}
};
为什么使用emplace
而不是push
使用emplace
将直接在容器内构造,避免了临时对象和拷贝。
为什么需要重载两个push函数
当实参为左值时,使用void push(const T &e)
传递一个常量左值引用可以避免不必要的拷贝,而当实参为右值时,使用void push(T &&e)
传递右值引用,这可以利用移动语义避免拷贝,另外还使用了std::forward
进行完美转发,使emplace
也能利用移动语义避免拷贝。
其他
// 存在虚假唤醒
cv.wait(lock);
// 使用带谓词判断的重载可以避免虚假唤醒
cv.wait(lock, [] -> bool {
return expr;
});
本文链接:
/archives/sMDCOpd3
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
Liccsu's blog!
喜欢就支持一下吧
打赏
微信