通过c++11的condition_variable实现的有最大缓存限制的队列
之前曾写过一个通过C++11的condition_variable实现的有最大缓存限制的队列,底层使用std::queue来实现,如果想要提升性能的话,可以考虑改用固定的长度环形数组。环形数组实现如下:
#include <cassert>
#include <type_traits>
#include <stdexcept> /*
* 文件名: circle_buffer
* 实现说明:底层使用数组来实现循环buffer
* (1) 当m_begIdx和m_endIdx相同时,表示数组为空,否则标识数组存在值
* (2) 通过预先多分配一个节点的方式,来实现存储count个元素的目的
*/ class empty_error : public std::logic_error {
explicit empty_error(const std::string& what_arg)
: logic_error(what_arg)
{} explicit empty_error(const char* what_arg)
: logic_error(what_arg)
{} }; class full_error : public std::logic_error {
explicit full_error(const std::string& what_arg)
: logic_error(what_arg)
{} explicit full_error(const char* what_arg)
: logic_error(what_arg)
{} }; template <typename T>
class circle_buffer {
public:
using size_type = size_t;
public:
explicit circle_buffer(size_type count)
: m_bufSize(count+),
m_buf(static_cast<T*>(std::malloc(sizeof(T)*m_bufSize))),
m_begIdx(),
m_endIdx()
{
assert(count >= );
if (m_buf == nullptr) {
throw std::bad_alloc();
}
} ~circle_buffer() {
clear(typename std::is_trivially_destructible<T>::type());
} size_t size() const noexcept {
if (m_endIdx < m_begIdx) {
return m_endIdx + m_bufSize - m_begIdx;
}
return m_endIdx - m_begIdx;
} bool empty() const noexcept {
return m_begIdx == m_endIdx;
} bool full() const noexcept {
return ((m_endIdx+) == m_begIdx) ||
(m_begIdx == && m_endIdx == getMaxIdx());
} // buffer最后插入一个值,这里会检查是否存在空间,如果不存在,则抛出异常
template <typename... Args>
void pushCheck(Args&&... args) {
if (full()) {
throw full_error("pushCheck invoked when buffer is full");
} push(std::forward<Args>(args)...);
} // buffer最后插入一个值,这里不做检查是否存在空间
template <typename... Args>
void push(Args&&... args) {
new (&m_buf[m_endIdx]) T(std::forward<Args>(args)...);
advanceIdx(m_endIdx);
} // buffer最前面取出一个值,这里会检查是否存在元素可以取出,如果不存在,则抛出异常
T popCheck() {
if (empty()) {
throw empty_error("popCheck invoked when buffer is empty");
} return pop();
} // buffer最前面取出一个值
T pop() {
auto val = std::move(m_buf[m_begIdx]);
clearOne(typename std::is_trivially_destructible<T>::type());
advanceIdx(m_begIdx);
return val;
} private:
// 将指示位置的序号前进一格
void advanceIdx(size_t& idx) noexcept {
if (idx == getMaxIdx()) {
idx = ;
} else {
++idx;
}
} // 非trivially析构函数类型
void clear(std::false_type) {
while (m_begIdx != m_endIdx) {
m_buf[m_begIdx].~T();
advanceIdx(m_begIdx);
}
std::free(m_buf);
} // trivially析构函数类型
void clear(std::true_type) {
std::free(m_buf);
} // 非trivially析构函数类型
void clearOne(std::false_type) {
m_buf[m_begIdx].~T();
} // trivially 析构函数类型
void clearOne(std::true_type) {
} size_t getMaxIdx() const noexcept {
return m_bufSize-;
} private:
size_type m_bufSize;
T* m_buf;
size_type m_begIdx;
size_type m_endIdx;
};
关于上面的环形数组,简单的单元测试代码如下,这里使用了catch2,如下代码需要放在.cpp文件中。
#define CATCH_CONFIG_MAIN
// This tells Catch to provide a main() - only do this in one cpp file
#include "catch.hpp"
#include "circle_buffer.h" TEST_CASE("circle buffer manipulation", "[circle_buffer]") {
circle_buffer<int> cb(); REQUIRE( cb.size() == );
REQUIRE( cb.empty() == true);
REQUIRE( cb.full() == false); cb.push();
cb.push(); REQUIRE( cb.size() == );
REQUIRE( cb.empty() == false );
REQUIRE( cb.full() == true ); auto dropFirst = cb.pop(); REQUIRE( dropFirst == );
REQUIRE( cb.size() == );
REQUIRE( cb.empty() == false );
REQUIRE( cb.full() == false ); cb.push(); REQUIRE( cb.size() == );
REQUIRE( cb.empty() == false );
REQUIRE( cb.full() == true); auto dropSecond = cb.pop(); REQUIRE( dropSecond == );
REQUIRE( cb.size() == );
REQUIRE( cb.empty() == false );
REQUIRE( cb.full() == false ); auto dropThird = cb.pop(); REQUIRE( dropThird == );
REQUIRE( cb.size() == );
REQUIRE( cb.empty() == true );
REQUIRE( cb.full() == false );
}
下面是基于环形数组实现的有最大长度限制的生产者消费者队列,注意一点,在使用下面队列时,编译选项要加上-std=c++11。
#include <condition_variable>
#include <chrono>
#include "circle_buffer.h" template <typename T>
class producer_consumer_queue {
public:
producer_consumer_queue(int maxSize): m_buffer(maxSize) { } // 处理数据线程
T readQueue() {
T data;
// 取出数据,然后处理数据
{
std::unique_lock<std::mutex> lock(m_queueMtx);
m_consumeCv.wait(lock, [this] { return !m_buffer.empty(); }); data = m_buffer.pop();
}
m_produceCv.notify_one(); return data;
} // 生产数据线程,返回值表示是否生产成功,如果超时就不会生产成功
template <typename Rep, typename Period, typename ...Args>
bool writeQueue(const std::chrono::duration<Rep, Period>& wait_time, Args&& ...args) {
// 预设一个消费者处理这个数据
{
std::unique_lock<std::mutex> lock(m_queueMtx);
auto success = m_produceCv.wait_for(lock, wait_time, [this] { return !m_buffer.full(); });
if (!success) {
return false;
}
m_buffer.push(std::forward<Args>(args)...);
}
m_consumeCv.notify_one();
return true;
} private:
// 用来缓存数据
circle_buffer<T> m_buffer;
// 用来保护数据
std::mutex m_queueMtx;
// 用来提醒当前可以消费
std::condition_variable m_consumeCv;
// 用来提醒当前可以生产
std::condition_variable m_produceCv;
};
以上就是这个队列的具体实现。之后,考虑写一些关于中间件的知识,可能会从grpc开始吧。
通过c++11的condition_variable实现的有最大缓存限制的队列的更多相关文章
- c+11 std::condition_variable and mutex
multiple threads synchronization primitive: 多线程同步语义 多线程的同步语义是多线程编程的核心,线程之间通过同步语义进行通信,实现并发.C++ JAVA 中 ...
- C++11 thread condition_variable mutex 综合使用
#include <mutex> #include <condition_variable> #include <chrono> #include <thre ...
- 【记录一个问题】ndk下使用c++11的condition_variable问题较多
1.存在通知丢失的情况:生产者线程通知196次,消费者线程收到190次,导致部分数据无法被处理. 2.cond.wait()方法后的加锁有问题,导致对空队列进行出队操作然后coredump.一直记得w ...
- 基于std::mutex std::lock_guard std::condition_variable 和std::async实现的简单同步队列
C++多线程编程中通常会对共享的数据进行写保护,以防止多线程在对共享数据成员进行读写时造成资源争抢导致程序出现未定义的行为.通常的做法是在修改共享数据成员的时候进行加锁--mutex.在使用锁的时候通 ...
- C++11新特性之线程操作
C++11之前没有对并发编程提供语言级别的支持,这使得我们在编写可移植的并发程序时,存在诸多的不便.现在C++11增加了线程以及线程相关的类,很方便地支持了并发编程,使得编写的多线程程序的可移植性得到 ...
- 浅谈C++11中的多线程(三)
摘要 本篇文章围绕以下几个问题展开: 进程和线程的区别 何为并发?C++中如何解决并发问题?C++中多线程的基本操作 浅谈C++11中的多线程(一) - 唯有自己强大 - 博客园 (cnblogs.c ...
- 20191127 Spring Boot官方文档学习(4.11)
4.11.使用NoSQL技术 Spring Data提供了其他项目来帮助您访问各种NoSQL技术,包括: Redis MongoDB Neo4J Solr Elasticsearch Cassandr ...
- shared_ptr和多线程
前一篇文章写得实在太挫,重新来一篇. 多线程环境下生命周期的管理 多线程环境下,跨线程对象的生命周期管理会有什么挑战?我们拿生产者消费者模型来讨论这个问题. 实现一个简单的用于生产者消费者模型的队列 ...
- Web Server 和 HTTP 协议
https://toutiao.io/posts/xm2fr/preview 一直在找实习,有点什么东西直接就在evernote里面记了,也没时间来更新到这里.找实习真是个蛋疼的事,一直找的是困难模式 ...
随机推荐
- 转载 * jQuery实现动态分割div—通过拖动分隔栏实现上下、左右动态改变左右、上下两个相邻div的大小
由jQuery实现上下.左右动态改变左右.上下两个div的大小,需要自己引入jquery1.8.0.min.js包 可用于页面布局. //============================ind ...
- Windows10 VS2017 C++多线程传参和等待线程结束
#include "pch.h" #include <iostream> #include <windows.h> using namespace std; ...
- ENVI5.3 影像重采样 和 tiff 保存
输入---之前用envi4.5处理后的2013分类影像---输出重采样的影像 直接在工具栏搜索 resize data---出来对话框, 这里有几种方法----sample line 指的行列号,可以 ...
- hyperscan应用参数
>>hs_compile_ext_multi 使用额外的参数编译表达式, 额外的参数包括: MIN_OFFSET 距离开始的最小偏移开始匹配 MAX_OFFSET 距离开始的最大偏移结束匹 ...
- python 启动shell报错 Subprocess Startup Error
如图所示启动python的IDLE以后,按F5启动module或者启动shell的时候就会启动失败提示上面错误,刚开始百度知道解决方案不靠谱,最后找到原因是我之前看小甲鱼的教学视频时,新建了一个pic ...
- MySQL-8.0.11与Navicat Premium安装教程
1. 下载MySQL 下载地址: https:////dev.mysql.com/downloads/mysql/ 百度云 链接:https://pan.baidu.com/s/1bxAtnvChZZ ...
- vue 项目搭建步骤
环境搭建步骤: 打开git ,运行 npm install --global vue-cli 这是安装vue的命令行 vue init webpack vue-demo 这是vue基于webpack的 ...
- echarts数据区域缩放(鼠标滚轮、滚动条、拉选框)
当一个echarts图表上的数据很多时,想要查看部分区域的数据状态,可以通过数据区域缩放来实现,现总结三个方法: 鼠标滚轮缩放: var arr = []; for(var i = 0;i<15 ...
- vue 数字随机滚动(数字递增)
html: <span v-for="i in numArr">{{i}}</span> data: numArr: [], methods: perN ...
- hql和sql的一些区别
日期格式化查询的区别: selectSql.append(" AND DATE_FORMAT(o.createDate,\'%Y-%m-%d\') = :createDate"); ...