固定尺寸内存块的缓冲队列类及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,没有涉及到商品库存),目前用户量还没有上来,目前没有出现问题,但是想到如果用户量变大,下单并发量变大,可能会出现一系列的问题,趁着空闲 ...
随机推荐
- JavaScript中将html字符串转化为Jquery对象或者Dom对象
实例代码: $('<a href="javascript:void(0);" onclick="showUI(this,"4028f65d5d1bb627 ...
- HDU 2825 Wireless Password【AC自动机+DP】
给m个单词,由这m个单词组成的一个新单词(两个单词可以重叠包含)长度为n,且新单词中包含的基本单词数目不少于k个.问这样的新单词共有多少个? m很小,用二进制表示新单词中包含基本单词的情况. 用m个单 ...
- ubuntu添加默认路由才可以访问网络
- 为什么空格拷贝到linux 会变成两个
为什么空格拷贝到linux 会变成两个 学习了:https://zhidao.baidu.com/question/266438357.html 在vi界面内输入:set paste 然后进行拷贝: ...
- [Backbone] Parse not formatted JSON code
The good Dr. recently had another team implement the server and they slightly messed up the format o ...
- [Node.js]32. Level 7: Working with Lists -- Redis
As we saw in the video, redis can do more than just simple key-value pairs. We are going to be using ...
- C#类似版本号有多个分割符可以产生的排列组合,类似版本号比较
我采用asp.net进行演示 送给有缘人吧,可以获得类似版本号的功能,也可以对比两个版本号,我这里是其他需要用逗号分割的 using System; public partial class _Def ...
- 网上下载的 chm 文件打开后右侧内容显示空白
有时候在网上下载的chm文件打不开,或者打开后右侧内容显示空白,可尝试以下方法解决. 1.当你第一次打开文件时,会弹出如下警告窗口,点击打开: 打开后发现不管你怎么点,右边始终是空白的,有时候也会提示 ...
- java发送email(含代理方式,ssl方式,传统方式)
package spring.vhostall.com; import java.security.Security; import java.util.Date; import java.util. ...
- Flash:DisplayObject的transform/matrix的潜规则、小bug
AS3中,使用DisplayObject的transform/matrix,需要先clone出来,再变换,再赋值回去,这样才会对DisplayObject产生影响,不能直接对原Matrix操作. ...