固定尺寸內存塊的緩沖隊列類及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++實現源代碼