通过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里面记了,也没时间来更新到这里.找实习真是个蛋疼的事,一直找的是困难模式 ...
随机推荐
- 高分一号计算NDVI
1.准备工作 如果想要ENVI友好一点,请下载ENVI5.3,并且安装China satellites 补丁(参考博客:ENVI扩展工具:中国国产卫星支持工具) App Store for ENVI下 ...
- eclipse使用技巧心得分享
eclipse使用技巧心得分享 习惯了eclipse开发java程序,公司最近的项目都是idea开发的,同时android studio也是idea原型开发的,在学android开发,所以脱离ec ...
- jQuery图片自动添加水印插件
JS脚本(jQuery)为图片加水印效果预览:http://hovertree.com/texiao/jquery/94/ 本功能使用HTML5实现,可为图片加上文字水印,可设置文字,设置颜色,位置等 ...
- SQLI DUMB SERIES-20
(1)登录成功后页面: (2)登录成功后,用burp开始抓包,刷新浏览器页面,将会跳出如下页面 (3)根据各种提示,知道需要从cookies入手,寻找闭合方式 闭合方式为单引号.注释符也可以用 (4) ...
- 评测parser的好坏
1.在dependency parsing中一般是用 LAS UAS 来衡量 简要说来UAS是知道是边对了(也就是它依赖的节点找对了)就算对,而LAS在前者的基础上要求更加严格,还要求边的Label也 ...
- hdu 1372 BFS
A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the sh ...
- centos6.5-VMware虚拟机-双网卡绑定
1 添加多张网卡(生产环境中有多个卡槽,可用ifconfig查看) 2 编辑两张虚拟机的网卡和物理机的连接方式,这里两张都使用NAT即可 3 打开虚拟机查看所有的网卡(网络接口),修改配置网卡配置文件 ...
- creator Box2d的相关物理问题
项目的屏幕配置为 1152*640,没有对这个数值调整来测试质量的除数1024会不会变化 m = h * w / 1024 (m 质量,h 物体高度,w 物体宽度) g = 0, -320 ( ( ...
- node环境下express路由,
1.基本路由概念 路由是指确定应用程序如何响应对特定端点的客户端请求,该请求是URI(或路径)和特定HTTP请求方法(GET,POST等). 每个路由都可以有一个或多个处理函数,这些函数在路由匹配时执 ...
- BigData_Jia
#include <stdio.h> #include <string.h> #define MaxSize 10000 int n1, n2, i, k, times; ch ...