固定尺寸内存块的缓冲队列类及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-10-19 04:36:45

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

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

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

并发编程(十)—— Java 并发队列 BlockingQueue 实现之 SynchronousQueue源码分析

BlockingQueue 实现之 SynchronousQueue SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样. 不像ArrayBlockingQueue或LinkedListBlockingQueue,SynchronousQueue内部并没有数据缓存空间,你不能调用peek()方法来看队列中是否有数据元素,因为数据元素只有当你试着取走的时候才可能存在,不取走而只想偷窥一下是不行

Cocos2dx-3.x 中CCCamera相机类详解及源码分析

Cocos2d-x 3.3版本中加入了相机这个类,该类在3D游戏中是必不可少的,在3D立体游戏中,往往需要视野角度的变化,通过相机的变换才能观察和体验整个游戏世界. CCCamera类基本使用 在游戏中一般有两种类型的相机:一种是透视相机,它在3D游戏中十分常见:另一种是正交相机,它没有透视相机的近大远小的效果而是相机内任何位置的物体大小比例都是一样的. 上图是透视相机的原理图,一般来说,我们通过以下代码创建: _camera = Camera::createPerspective(60, (G

ThreadLocal类的用法与源码解析

首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各个线程中访问的是不同的对象. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.通过ThreadLocal.set()将这个新创建

茶叶百科类通用应用安卓源码

茶叶百科类通用源码主要是介绍与茶相关的一些知识,高仿的茶百科官方应用搜索一下“茶百科+百度应用”就可以找到,其实这一类的应用有普遍性,例如可以起名茶百科那就可以起名其他百科,无非是替换一下标题.图标和内容,大体的基本框架还是不用动的.本项目内数据直接调用http://sns.maimaicha.com/数据,使用json与网站做的交互. <ignore_js_op> <ignore_js_op> 详细说明:http://android.662p.com/thread-5937-1-

7.Java集合-Arrays类实现原理及源码分析

Java集合---Arrays类源码解析 转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型:采用改进的归并排序. 1.对于基本类型源码分析如下(以int[]为例): Java对Primitive(int,float等原型数据)数组采用快速排序,对

mybatis generator为实体类生成自定义注释(读取数据库字段的注释添加到实体类,不修改源码)

我们都知道mybatis generator自动生成的注释没什么实际作用,而且还增加了代码量.如果能将注释从数据库中捞取到,不仅能很大程度上增加代码的可读性,而且减少了后期手动加注释的工作量. 1.首先定义注释生成插件 MyCommentGenerator.java package com.ilovey.mybatis.comment; import org.mybatis.generator.api.IntrospectedColumn; import org.mybatis.generato

使用WebClient类对网页下载源码,对文件下载保存及异步下载并报告下载进度

private void button1_Click(object sender, EventArgs e) { WebClient webclient = new WebClient(); webclient.Proxy = null; webclient.Encoding = Encoding.UTF8; richTextBox1.AppendText(webclient.DownloadString(textBox1.Text.Trim())); webclient.Dispose();

打造vim成类source insight——contiki源码阅读工具

一.Ubuntu14.04下配置 1.配置vimrc文件 输入:version课查看vimrc文件及位置: system vimrc file: "$VIM/vimrc" user vimrc file: "$HOME/.vimrc“(建议放置位置) 2nd user vimrc file: "~/.vim/vimrc" user exrc file: "$HOME/.exrc" system gvimrc file: "$V