通过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里面记了,也没时间来更新到这里.找实习真是个蛋疼的事,一直找的是困难模式 ... 
随机推荐
- 进程PCB
			struct task_struct { volatile long state; //说明了该进程是否可以执行,还是可中断等信息 unsigned long flags; //Flage 是进程号, ... 
- C#     array与arraylist区别及获取sql字段名
			array与arraylist的区别: 1. Array 的容量是固定的,而 ArrayList 的容量是根据需要自动扩展的.如果更改了 ArrayList.Capacity 属性的值,则自动进行内 ... 
- Mac 安装Python3 facewap环境
			参考网上大神的方法 1 官网下载安装 2 下载指定版本的源码cmake安装 3 Mac上使用homebrew进行安装(强烈推荐,主要是前两种的openssl模块我没有搞定链接什么的一直报错,一个个下载 ... 
- Js/如何修改easyui修饰的input的val值
			1.关于js对input值的修改介绍:一般js改变input的val值,我一直使用的方法是: $('#id').val('test');这样的方式来进行修改.但是我使 用了class="ea ... 
- html基础js
			HTML中的三把利器的JS 又称为JavaScript,看着好像和Java有点联系,实际上他和java半毛钱关系都没有,JavaScript和我们学习的Python.Go.Java.C++等,都是一种 ... 
- 前端导出csv
			前端导出csv export: function(data, name) { // csv文件的BOM头 \ufeff可以让excel等识别出csv文件的编码 var uri = 'data:text ... 
- java jdk jre
			Java11新特性: https://www.cnblogs.com/eric-shao/p/10025180.html java的一些基本概念——java11.jdk.jre.jvm: https: ... 
- 我在web前端路上的第一个脚印
			这是我的第一篇博客,希望记录下我在路上见到的风景,也和大家一起分享. 
- spring定时器cron
			关于cron表达式(参考资料):Cron 表达式包括以下 7 个字段: 秒 分 小时 月内日期 月 周内日期 年(可选字段) 特殊字符Cron 触发器利用一系列特殊字符,如下所示: 反斜线(/)字符表 ... 
- SP3871 GCDEX - GCD Extreme
			//author Eterna #define Hello the_cruel_world! #pragma GCC optimize(2) #include<iostream> #inc ... 
