固定尺寸内存块的缓冲队列类及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);
   }
}

}

--------------------------------------------------------------------------------

时间: 2024-12-24 02:23:04

固定尺寸内存块的缓冲队列类及C++实现源代码的相关文章

固定尺寸内存块的缓冲队列类及C++实现源码

-------------------------------------------------------------------------------- 标题: 固定尺寸内存块的缓冲队列类及实现源码 作者: 叶飞虎 日期: 2014.10.21 -------------------------------------------------------------------------------- 在一般的线性操作应用中(如: 接收缓冲区), 可能需要频繁分配和释放内存块, 频繁操

固定内存块尺寸的内存池原理及代码

有些情形会需要申请大量的固定尺寸的内存块,若一个个都用malloc申请效率很低,这种情况非常适合使用内存池解决. 下面就是一个固定内存块尺寸的内存池的完整源码.注:其中的内存申请不是使用的malloc,而是自己定义的torch::HeapMalloc,简单修改下即可. 代码详情请见Github[点击] 先看代码再看讲解 /* * 固定尺寸的内存池 * 说明: * - 只适用于需要大量固定尺寸内存块的情况 */ template<size_t _Size> class FixedMemoryPo

Objective-c 05 类别 类扩展 委托 非正式协议 协议 代码块 并发性 队列

类别 为已经存在的类添加行为时,通常采用创建子类的方法,不过有时子类并不方便, 比如:创建NSString的子类,但是NSString实际上只是一个类簇的表面形式.因而为这样的类创建子类会非常困难.在其他情况下, 也许可以创建它的子类,但是用到的工具集和库无法帮你处理新类的对象的.例如:当使用stringWithFormat:类方法生成新字符串时,你创建的 NSString类的新子类就无法返回.   利用Objective-C的动态运行时分配机制,可以为现有的类添加新的方法.这些新的方法在Obj

操作系统课程设计 消息缓冲队列通信

消息缓冲队列通信机制其基本思想是根据“生产者——消费者”原理,利用内存中公用消息缓冲区实现进程间的信息交换. 在这种通信机制中,首先需要在内存中开辟若干空闲消息缓冲区,用以存放要通信的消息.每当一个进程需要向另一个进程发送消息时,便向系统申请一个空闲消息缓冲区,并把已准备好的消息复制到该缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程.接收进程接收到发送进程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出其中的信息,然后把消息缓冲区作为空闲消息缓冲区归还给系统.系统

通过系统缓冲流类提高I/O操作

Java语言中一般的输入与输出流类,都是采用单字节的读取方法,进行数据I/O操作的.也就是每次只读取或写入一个字节的数据,这种方法显然繁琐而低效.其读取与写入的流程图,如图1所示: 打个比方: 假设在A地有100本图书,需要把这些图书从A地运送到B地. 如果每次从A地只拿一本图书到B地,则需要往返A.B两地100次: 如果每次从A地取10本图书到B地,则只需要10次就可以完成这个任务. 显然,后者的效率要高.这个故事中的"每次拿10本"相当于I/O操作的"缓冲区".

多线程操作C++ STL vector出现概率coredump问题及尽量避免锁的双缓冲队列

多线程操作全局变量,必须考虑同步问题,否则可能出现数据不一致, 甚至触发coredump. 前段时间, 遇到一个多线程操作了全局的vector的问题,  程序崩了.场景是这样的:某全局配置参数保存在一个vector中,需要定时更新(更新线程), 另外的工作线程去读取配置. 这种场景是非常普遍的. 在该场景中,程序没有枷锁,概率coredump, 实际情况是,服务跑了一段时间后,必然coredump.   很显然, 更新线程执行clear,然后在push_back操作时, 会导致工作线程的vect

双缓冲队列-减少生产者消费者锁的调用

在生产者-消费者模式中,我们常常会使用到队列,这个队列在多个线程共享访问时存在互斥和竞争操作, 意味着每次访问都要加锁.如何更好的如何减少锁竞争次数呢 ?今天要介绍的双缓冲队列就是个不错的选择. 双缓冲队列就是冲着同步/互斥的开销来的.我们知道,在多个线程并发访问同一个资源的时候,需要特别注意线程的同步问题.稍稍不注意,噢货,程序结果不正确了. 原理 直接上图: 这样为什么会减少锁的调用呢? 举个例子, 两个List,一个用来存,一个用来取.有点迷糊?就是有一个listP从工厂那里得到玩具对象,

一个队列类的实现 转

一个队列类的实现(比delphi自带的速度快70倍) 测试结果 实现的代码 unit sfContnrs; interface {$DEFINE MULTI_THREAD_QUEUE} //线程安全版本,如果不需要线程安全,请注释掉此行代码 {$IFDEF MULTI_THREAD_QUEUE}uses    Windows;{$ENDIF} type  TsfQueue=class  private    FCapacity:Integer;    FTmpBuff:Pointer;    F

C++学习笔记50:队列类模板

队列是只能向一端添加元素,从另一端删除元素的线性群体 循环队列 在想象中将数组弯曲成环形,元素出队时,后继元素不移动,每当队尾达到数组最后一个元素时,便再回到数组开头. 队列类模板 //Queue.h #ifndef QUEUE_H #define QUEUE_H #include <cassert> //类模板的定义 template <class T, int SIZE = 50> class Queue { private: int front, rear, count; T