固定尺寸内存块的缓冲队列类及C++实现源代码
--------------------------------------------------------------------------------
标题: 固定尺寸内存块的缓冲队列类及实现源代码
作者: 叶飞虎
日期: 2014.10.21
--------------------------------------------------------------------------------
在一般的线性操作应用中(如: 接收缓冲区), 可能须要频繁分配和释放内存块, 频繁操
作会给系统带来非常大开销, 怎样降低系统开销? 通过拉大分配和释放之间间距来降低操作的
频度, 从而达到降低系统开销。
拉大分配和释放之间间距的方法有非常多, 能够通过大内存块自己管理, 也能够通过内存
块缓冲队列。
本文着重讲内存块缓冲队列, 涉及队列就会考虑到无锁进出队列, 即进队列和
出队列在二个线程中能够同一时候操作。
无锁队列的实现方法有非常多, 有数组方式的环形无锁队
列, 也有链接方式的无锁队列。
数组方式在队列容量确定时比較适合, 而链接方式更适合于
队列容量可变情况, 适用性更好。
数组方式无锁队列见我的博文 <在一读一写限制下,无锁环形队列怎样实现?>
链接方式无锁队列见我的博文 <一读一写情况下。无锁队列怎样实现?>
本文讲的缓冲队列为链接方式, 链接方式一般通过预分配一个结点作为接力点来实现无
锁队列, 长处是实现简单, 缺点是浪费一个结点的内存, 当结点内存块尺寸较大时浪费就大
了。怎样不浪费一个结点内存的链接方式无锁队列? 当队列中仅仅有一个结点时, 本缓冲队列
中使用了原子锁进行操作, 这是一种平衡策略, 若读者有更好方法最好还是告之中的一个下!
固定尺寸内存块的缓冲队列类(TKYCache)源代码例如以下:
// =======================================
// Unit : 固定尺寸的内存块缓冲
// Version: 3.0.0.0 (build 2014.10.21)
// Author : Kyee Ye
// Email : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// ======================================= #ifndef _KYCache_H_
#define _KYCache_H_ #include "KYObject.h" // KYLib 2.0 開始使用 KYLib 命名空间
namespace KYLib
{ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* TKYCache - 固定尺寸的内存块缓冲类 */ // 注:
// 1. 为了多线程存取安全, New 和 Delete 分属二个线程时能够同一时候操作而不须要加锁,
// 但多线程 New 时必须用锁控制, 多线程 Delete 时必须用锁控制!
// 2. 此缓冲类一般应用于线性操作的类中, 以降低频繁分配和释放内存的缓冲使用. class TKYCache
{
private:
// 内存块的链接
typedef struct
{
void* Self; // 内存块所属对象
void* Next; // 下一块
} TLink, *PLink; public:
// 构造函数
// 1. ABlockSize 内存块的固定尺寸, 取值范围: [0x40..0x40000000]
// 2. AMaxCount 内存块缓冲的最大个数
TKYCache(long ABlockSize = 1024, long AMaxCount = 256);
virtual ~TKYCache(); // 属性
long Count() const { return FPushCount - FPopCount; }
long MaxCount() const { return FMaxCount; } // default: AMaxCount
long BlockSize() const { return FBlockSize; } // default: ABlockSize // 设置内存块缓冲的最大个数
void SetMaxCount(long AMaxCount)
{ FMaxCount = (AMaxCount >= 0) ? AMaxCount : 0; } // 分配固定尺寸的内存块
void* New()
{
TLink* pItem = DoNew();
return (pItem != NULL) ? (char*)pItem + sizeof(TLink) : NULL;
} // 释放固定尺寸的内存块
void Delete(void* ABlock)
{
if (ABlock != NULL)
{
TLink* pItem = (TLink*)((char*)ABlock - sizeof(TLink));
if (pItem->Self == this)
DoDelete(pItem);
}
} private:
// 运行分配/释放带链接的内存块
TLink* DoNew();
void DoDelete(TLink* ALink); // 运行清除缓冲队列
void DoClear(TLink* AHead); private:
TLink* FHead; // 缓冲的头链接
TLink* FTail; // 缓冲的尾链接
long FFlag; // 缓冲队列标志
long FMaxCount; // 缓冲最大个数
long FBlockSize; // 内存块的尺寸
Longword FPushCount; // 压入缓冲计数
Longword FPopCount; // 弹出缓冲计数
}; } #endif
// =======================================
// Unit : 固定尺寸的内存块缓冲
// Version: 3.0.0.0 (build 2014.10.21)
// Author : Kyee Ye
// Email : kyee_ye(at)126.com
// Copyright (C) Kyee workroom
// ======================================= #include <malloc.h>
#include "KYCache.h" // KYLib 2.0 開始使用 KYLib 命名空间
namespace KYLib
{ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* TKYCache - 固定尺寸的内存块缓冲类 */ // ---------------- 构造函数和析构函数 ----------------
// 构造函数
TKYCache::TKYCache(long ABlockSize, long AMaxCount)
{
// 初始化
FHead = NULL;
FTail = NULL;
FFlag = 0;
FPushCount = 0;
FPopCount = 0; // 设置缓冲最大个数
FMaxCount = (AMaxCount >= 0) ? AMaxCount : 0; // 设置内存块的尺寸
if (ABlockSize <= 0x40)
FBlockSize = 0x40;
else if (ABlockSize <= 0x40000000)
FBlockSize = ABlockSize;
else
FBlockSize = 0x40000000;
} // 析构函数
TKYCache::~TKYCache()
{
// 运行清除缓冲队列
if (FPopCount != FPushCount)
{
FPopCount = FPushCount;
DoClear(FHead);
}
} // ---------------- 私有函数 ----------------
// 运行分配带链接的内存块
TKYCache::TLink* TKYCache::DoNew()
{
// 初始化
TLink* result = NULL; // 推断缓冲队列是否为空
if (FPopCount == FPushCount)
result = (TLink*)malloc(sizeof(TLink) + FBlockSize);
else if (FPushCount - FPopCount != 1)
{
// 取第一项, 而且计数加 1
result = FHead;
FHead = (TLink*)result->Next;
FPopCount++;
}
else
{
// 取第一项
result = FHead; // 推断是否须要等待, 防止 DoDelete 冲突
if (InterlockedIncrement(&FFlag) == 1)
{
FPopCount++;
if (FPopCount == FPushCount)
{
FHead = NULL;
FTail = NULL;
}
InterlockedDecrement(&FFlag);
}
else
{
FPopCount++;
InterlockedDecrement(&FFlag); // 循环等待 FPushCount 变化
while (FPopCount == FPushCount)
Sleep(1);
} // 改动缓冲的头链接
if (result->Next != NULL)
FHead = (TLink*)result->Next;
} // 初始化链接项
if (result != NULL)
{
result->Self = this;
result->Next = NULL;
} // 返回结果
return result;
} // 运行释放带链接的内存块
void TKYCache::DoDelete(TLink* ALink)
{
// 推断是否已满
if (FPushCount - FPopCount >= (Longword)FMaxCount)
free(ALink);
else
{
// 置空
ALink->Next = NULL; // 引用计数加 1, 若不等于 1 则等待 DoNew 变成 1
if (InterlockedIncrement(&FFlag) != 1)
while (FFlag != 1)
Sleep(0); // 推断是否为第一项
if (FTail == NULL)
{
FTail = ALink;
FHead = ALink;
}
else
{
FTail->Next = ALink;
FTail = ALink;
} // 计数加 1, 且引用计数减 1(注: 顺序不能改, 否则可能会导致DoNew死循环)
FPushCount++;
InterlockedDecrement(&FFlag);
}
} // 运行清除缓冲队列
void TKYCache::DoClear(TLink* AHead)
{
// 初始化
void* pCurr; // 循环释放
while (AHead != NULL)
{
pCurr = AHead;
AHead = (TLink*)AHead->Next; // 释放
free(pCurr);
}
} }
--------------------------------------------------------------------------------
固定尺寸内存块的缓冲队列类及C++实现源代码的更多相关文章
- 嵌入式无操作系统下管理内存和队列(类UCOS II思想)
例子:存储日志,最多存128条,每条最大1MB. 内存方面 因为嵌入式不适合用动态内存,会产生碎片.这里我们用 u8 data[LOG_SIZE];开辟固定128MB的内存区,再对其分为128个1MB ...
- How Javascript works (Javascript工作原理) (三) 内存管理及如何处理 4 类常见的内存泄漏问题
个人总结: 1.两种垃圾回收机制: 1)引用标记算法:如果检测到一个对象没有被引用了,就清除它. ***这种算法不能处理循环引用的情况*** 2)标记—清除算法:从根(全局变量)开始向后代变量检测,任 ...
- JavaScript 工作原理之三-内存管理及如何处理 4 类常见的内存泄漏问题(译)
原文请查阅这里,本文有进行删减,文后增了些经验总结. 本系列持续更新中,Github 地址请查阅这里. 这是 JavaScript 工作原理的第三章. 我们将会讨论日常使用中另一个被开发者越来越忽略的 ...
- Java并发编程:4种线程池和缓冲队列BlockingQueue
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动 ...
- dsp下基于双循环缓冲队列的视频采集和显示记录
对最近在设计的视频采集和显示缓冲机制做一个记录,以便以后使用. 视频采集和显示缓冲机制,其实是参考了Linux下v4L2的驱动机制,其采用输入多缓冲frame,输出多缓冲的切换机制.简单的就是ping ...
- 自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求
.原文:https://blog.csdn.net/u011677147/article/details/80271174 拓展: https://github.com/jwpttcg66/GameT ...
- 多线程操作C++ STL vector出现概率coredump问题及尽量避免锁的双缓冲队列
多线程操作全局变量,必须考虑同步问题,否则可能出现数据不一致, 甚至触发coredump. 前段时间, 遇到一个多线程操作了全局的vector的问题, 程序崩了.场景是这样的:某全局配置参数保存在一 ...
- 【LA2238 训练指南】固定分区内存管理 【二分图最佳完美匹配,费用流】
题意 早期的多程序操作系统常把所有的可用内存划分为一些大小固定的区域,不同的区域一般大小不同,而所有区域的大小之和为可用内存的大小.给定一些程序,操作系统需要给每个程序分配一个区域,使得他们可以同时执 ...
- SpringBoot项目框架下ThreadPoolExecutor线程池+Queue缓冲队列实现高并发中进行下单业务
主要是自己在项目中(中小型项目) 有支付下单业务(只是办理VIP,没有涉及到商品库存),目前用户量还没有上来,目前没有出现问题,但是想到如果用户量变大,下单并发量变大,可能会出现一系列的问题,趁着空闲 ...
随机推荐
- 【Type】类型 ParameterizedType
Type 接口[重要] Type接口完整的定义: public interface java.lang.reflect.Type { /** * Returns a string describing ...
- 简单JavaScript语句实现搜索关键字高亮功能
高亮功能主要是指对页面中指定区域的指定文字进行高亮显示,也就是背景着色.一般在搜索结果页面会经常用到这个功能. 下面就为大家提供一种解决方案,用javascript实现. 首先在<head> ...
- C# 开发者代码审查清单
这是为C#开发者准备的通用性代码审查清单,可以当做开发过程中的参考.这是为了确保在编码过程中,大部分通用编码指导原则都能注意到.对于新手和缺乏经验(0到3年工作经验)的开发者,参考这份清单编码会很帮助 ...
- 读取Mat文件中的汉字代码
拿到一种元数据,格式为*.mat,但Access打不开,百度也没看到比较好的解决方案. 但是用文本文档可以打开,那估计它和txt类似了,于是想自己写代码来处理了,立马写了读取的丑陋工具.读取是没什么问 ...
- 公众号 - 解决所有测试中的CORS问题
仅支持GET请求,POST请求会报错. 软件:Chrome 插件:CORS 点击下载 演示:
- Mac Oracle SqlDeveloper 快捷输入
用惯了 plsql 的快捷输入,换了 Mac Oracle SqlDeveloper反倒是找不到了,翻出去找了几次终于找到 SqlDeveloper -- preferences -- 数据库(dat ...
- HTML的GET方法传递参数样式。
#HTML的GET方法传递参数样式.http://127.0.0.1:8080/web1/urlinfo/getobject.html?UserId=1&UserName=ad
- jQuery实现页面关键词高亮
示例代码,关键位置做了注释,请查看代码: <html> <head> <title>jQuery实现页面关键词高亮</title> <style ...
- HTTP Header具体解释
HTTP(HyperTextTransferProtocol) 即超文本传输协议,眼下网页传输的的通用协议.HTTP协议採用了请求/响应模型,浏览器或其它client发出请求.server给与响应.就 ...
- 【BIRT】使用rptlibrary设置统一数据源
在使用birt开发报表的过程中我们通常会发现,如果直接在报表中写死数据源的话,那么之后如果数据库服务器发生变更,那么所有的报表都得一一去修改数据源 这是一个相当耗时间的过程. 1.首先新建librar ...