内存缓存是简单的在内存进行读写操作的一种传输,任何时候想在上面写入数据都是放入缓存中,任何时候读操作数据也是来至于缓存。内存缓存的分配使用c语言的malloc类函数,分配的长度是需要长度的两倍,需要考虑这些内存缓存的使用范围。

同样这个类继承至缓存基类TBufferBase,默认的缓存大小是1024(static const uint32_t defaultSize = 1024;),所有的构造函数都调用函数initCommon,这个函数实现如下:

 voidinitCommon(uint8_t* buf, uint32_t size, bool owner, uint32_t wPos) {
if (buf== NULL && size != 0) {//如果传递过来的buf为null并且size不为0
assert(owner);//断言这个内存缓存是不是自己所有,不是就报错不继续执行后面的代码了
buf =(uint8_t*)std::malloc(size);//是自己拥有的内存缓存就自己分配size大小的内存作为缓存
if(buf == NULL) {
throw std::bad_alloc();//分配失败抛出异常
}
}
buffer_= buf;//初始化缓存成员变量
bufferSize_ = size;//大小 rBase_= buffer_;基地址
rBound_= buffer_ + wPos;//界限 wBase_= buffer_ + wPos;//写缓存基地址
wBound_= buffer_ + bufferSize_;//写界限 owner_= owner;//是否自己拥有这段内存缓存
}

上面代码需要说明是:在初始化这个类的时候对于内存缓存是不是属于类自己拥有有不同的处理方式,后面介绍各个方针;还有一点就是读的边界其实就是写的起始地址,因为写入开始的地方就是没有有效数据的地方了,就是一个生存者和消费者的问题,这个后面介绍的一个函数computeRead会更新相关内容。下面我们就看看内存缓存采取的几种可配置的方针(对待内存缓存的方式),是定义一个枚举标识每一个方针的,定义如下:

 enumMemoryPolicy
{ OBSERVE= 1
, COPY =2
,TAKE_OWNERSHIP = 3
};

下面分配分析这三种方针,第一种称为观察方针,意思是内存缓存传输类只是简单的存储一个指针存放到内存,它是调用者来保证内存缓存在剩余的时间指向一个有效的内存,并且在适当的时候清理它(注意:这种内存缓存是不允许写入数据的);第二种是内存缓存类内部的一种拷贝,调用者无任何责任;最后一种就是内存缓存类自己拥有的内存缓存,这个必须自己释放,并且在使用以前需要使用malloc分配。明白了三种内存方针了,就知道什么时候需要的是哪一种。内存使用方针除了被构造函数使用以外,我们在重置内存缓存区时也会使用到,而重置缓存函数是resetBuffer,有两个函数重载。在实现重载函数的时候使用了一点点优化手段,就是交换内存的时候使用std::swap,这个函数的好处就是如果是相同的内存分配器分配的就直接交换指针头就可以了。

同其它缓存类一样,内存缓存类通常采用快读和快写去操作数据,这些操作基类的实现已经满足要求了,只是在缓存中数据不足时就会调用慢读和慢写函数从更底层的传输去拿数据,所以慢写和慢读函数实现是不相同的。我们现在就看看内存缓存类的慢读和慢写函数。慢读函数readSlow函数现实如下:

uint32_t TMemoryBuffer::readSlow(uint8_t* buf,uint32_t len) {
uint8_t*start;//缓存读取开始地址
uint32_tgive;//缓存可以读取的长度
computeRead(len, &start, &give);//计算缓存中可以读取的开始地址和长度 // 把缓存中的数据拷贝到提供的buf中
memcpy(buf, start, give);
returngive;//返回实际读取到的数据,可能小于len
}                                       

在读取数据以前先要计算可以读取的开始地址和长度(因为写函数会更新缓存中的数据),这个功能是函数computeRead实现,代码如下:

voidTMemoryBuffer::computeRead(uint32_t len, uint8_t** out_start, uint32_t*out_give) {
rBound_ = wBase_;//更新读取的边界,写的起始地址就是读取的最大限制地址
//取小者,因为数据可能不够,可利用的读长度就是写的起始地址减去读的起始地址
uint32_t give = std::min(len,available_read());
*out_start = rBase_;//返回的读起始地址
*out_give = give;//返回读取的可用长度
rBase_ += give;//更新读取的起始地址
}

下面继续分析慢写函数的实现,代码如下:

voidTMemoryBuffer::writeSlow(const uint8_t* buf, uint32_t len) {
ensureCanWrite(len);//确保能够写入
// 拷贝数据到写缓存的基地址
memcpy(wBase_, buf, len);
wBase_ += len;//更新写缓存的基地址
}

在写入之前需要保证是可以写入的,实现这个保证的函数是ensureCanWrite,代码如下:

void TMemoryBuffer::ensureCanWrite(uint32_tlen) {
uint32_t avail = available_write();//检查可以利用的缓存空间
if (len <= avail) {//如果空间足够就直接返回了
return;
} if (!owner_) {//检查内存是否自己拥有
throwTTransportException("Insufficient space in external MemoryBuffer");//抛出不是自己拥有不能写入
} // 根据需要增加缓存空间.
uint32_t new_size = bufferSize_;
while (len > avail) {//循环增加,每次都是上次的二倍大小,知道满足要求为止
new_size = new_size > 0 ? new_size * 2 :1;
avail = available_write() + (new_size -bufferSize_);//计算新的可利用空间:新-老+以前可利用
}
void* new_buffer = std::realloc(buffer_,new_size);//重新按照新大小分配缓存空间
if (new_buffer == NULL) {
throw std::bad_alloc();
}
bufferSize_ = new_size;//重置缓存大小 ptrdiff_t offset = (uint8_t*)new_buffer -buffer_;//计算偏移量,下面依次更新缓存值
buffer_ += offset;
rBase_ += offset;
rBound_+= offset;
wBase_ += offset;
wBound_ = buffer_ + bufferSize_;
}

内存缓存类还增加了一些额外的功能:例如按照字符串来读写,方便了对字符串的操作。

thrift之TTransport层的内存缓存传输类TMemoryBuffer的更多相关文章

  1. thrift之TTransport层的分帧传输类TFramedTransport

    帧传输类就是按照一帧的固定大小来传输数据,所有的写操作首先都是在内存中完成的直到调用了flush操作,然后传输节点在flush操作之后将所有数据根据数据的有效载荷写入数据的长度的二进制块发送出去,允许 ...

  2. thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase

    本节主要介绍缓冲相关的传输类,缓存的作用就是为了提高读写的效率.Thrift在实现缓存传输的时候首先建立一个缓存的基类,然后需要实现缓存功能的类都可以直接从这个基类继承.下面就详细分析这个基类以及一个 ...

  3. thrift之TTransport层的堵塞的套接字I/O传输类TSocket

    本节将介绍第一个实现具体传输功能的类TSocket,这个类是基于TCP socket实现TTransport的接口.下面具体介绍这个类的相关函数功能实现. 1.构造函数 分析一个类的功能首先看它的定义 ...

  4. 3.NetDh框架之缓存操作类和二次开发模式简单设计(附源码和示例代码)

    前言 NetDh框架适用于C/S.B/S的服务端框架,可用于项目开发和学习.目前包含以下四个模块 1.数据库操作层封装Dapper,支持多种数据库类型.多库实例,简单强大: 此部分具体说明可参考博客: ...

  5. thrift之TTransport类体系原理及源码详细解析1-类结构和抽象基类

    本章主要介绍Thrift的传输层功能的实现,传输的方式多种多样,可以采用压缩.分帧等,而这些功能的实现都是相互独立,和上一章介绍的协议类实现方式比较雷同,还是先看看这部分的类关系图,如下: 由上面的类 ...

  6. thrift之默认传输类TTransportDefaults和虚拟传输类TVirtualTransport

    默认传输类TTransportDefaults提供了抽象类TTransport的默认实现,实现了非虚拟的方法(*_virt) read(), readAll(), write(),borrow() a ...

  7. 基于.net的通用内存缓存模型组件

    谈到缓存,我们自然而然就会想到缓存的好处,比如: 降低高并发数据读取的系统压力:静态数据访问.动态数据访问 存储预处理数据,提升系统响应速度和TPS 降低高并发数据写入的系统压力 提升系统可用性,后台 ...

  8. Memcached内存缓存技术

    Memcached是什么,有什么作用? Memcached是一个开源的.高性能的内存缓存软件,从名称上看Mem就是内存的意思,而Cache就是缓存的意思. Memcached通过在事先规划好的内存空间 ...

  9. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

随机推荐

  1. RSA JS 加密解密DEMO

    <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script&g ...

  2. DevExpress.XtraReports.UI.XtraReport 动态报表

    原文:DevExpress.XtraReports.UI.XtraReport 动态报表 using System;using System.Collections.Generic;using Sys ...

  3. 大数据系列修炼-Scala课程09

    Option使用和实现内幕源码揭晓 1.Option中的sealed关键字解析:Option中用了sealed,定义的case class与case object必须在同一个文件中.Option在模式 ...

  4. hdu 质方数

    Problem Description   小明天生对数字比较敏感,3岁的时候就能背诵圆周率一百位. 现在,小明慢慢长大了,但依然很喜欢数字,最近,他迷上了质数和平方数,并且自己把质数的平方命名为“质 ...

  5. crawler_网络爬虫之数据分析_httpwatcher

    所谓爬虫,首先要通过各种手段爬取到想要站点的数据. web2.0之后,各种网络站点类型越来越多,早期的站点多为静态页面[html .htm],后来逐步加入 jsp.asp,等交互性强的页面.再后来随着 ...

  6. Unity插件之NGUI学习(8)—— Table和NGUI尺寸转换为世界坐标系尺寸

    依据 Unity插件之NGUI学习(2),创建一个UI Root,在UI Root下创建一个Texture作为背景图,并设置图片,在Wiget下调整大小:然后在UI Root下再创建一个Panel. ...

  7. 玩转Vim-札记

    玩转Vim-札记 距上篇博文已有一周有余,上次主要介绍了编辑器之神Vim的起源.安装并介绍了两种模式以及一些简单的操作.本次将继续对Vim的使用进行介绍. 登堂入室 首先接着说移动吧: 1 0 → 数 ...

  8. HTML5多图片拖拽上传带进度条

    前言 昨天利用css2的clip属性实现了网页进度条觉得还不错,但是很多情况下,我们在那些时候用进度条呢,一般网页加载的时候如果有需要可以用,那么问题就来了,怎么才算整个加载完毕呢,是页面主要模块加载 ...

  9. MongoDB在window下的安装

    1.下载mongodb的windows版本号,有32位和64位版本号,依据系统情况下载,下载地址:http://www.mongodb.org/downloads 2.解压缩至D:/mongodb就可 ...

  10. (工具)source insight高速增加时间代码

    这篇文章是程序代码更改由其他用户. 不是原厂原装,例如下列总结,使用作为个人笔记. (1)打开projectbase.打开文件Utils.em,插入下面代码: //插入时间 macro MonthTo ...