固定尺寸内存块的缓冲队列类及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); } } }
--------------------------------------------------------------------------------
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。