当前位置: 首页 > news >正文

高并发内存池(4)——实现CentralCache

目录

一,CentralCache的简单介绍

 二,CentralCache的整体结构

三,CentralCache实现的详细代码

1,成员

 2,函数

1, 获取单例对象的指针

2, FetchRangeObj函数

3,GetOneSpan函数实现

 4,ReleaseListToSpans函数实现


一,CentralCache的简单介绍

CentralCache是高并发内存池这个项目的中间层。当第一层ThreadCache内存不够时便会向这一层申请内存。而CentralCache这一层的内存是从下一层的PageCache层申请的。如下图所示:

可以看到,CentralCahe层的实现和ThreadCache层的实现有异曲同工之妙。不过,在CentralCache链接的是由span块链接而成的spanlist。 spanlist上面便链接着自由链表。

span实现如下:

//定义一个Span结构,是CentralCache里面的大号内存
struct Span
{PAGEID _pageid = 0; //页号:在不同的程序下要有不同的类型size_t _n = 0; //一个span里的页的数量//双向链表的结构Span* _prev = nullptr;Span* _next = nullptr;//使用的个数,当_usecount为0时表示无人使用。int _useCont = 0;//Span节点中的小内存void* _freelist = nullptr;bool _used = false;int _objSize = 0;};//一个成员为Span类型的带头双向循环链表
class Spanlist
{
public:Spanlist(){//_head = PageCache::GetInstance()->_Newtospan.New();// FixedPoll<Span> _fixedpoll;_head = new Span;_head->_next = _head;_head->_prev = _head;}void PushFront(Span* span){insert(begin(), span);}Span* PopFront(){return remove(begin());}//插入新的节点到某一个位置上void insert(Span* pos, Span* Newnode){assert(pos );assert(Newnode);Span* prev = pos->_prev;prev->_next = Newnode;Newnode->_prev = prev;Newnode->_next = pos;pos->_prev = Newnode;}//删除某个位置上的节点Span* remove(Span* pos){assert(pos);assert(pos != _head);Span* Next = pos->_next;Span* Prev = pos->_prev;Prev->_next = Next;Next->_prev = Prev;pos->_next = nullptr;pos->_prev = nullptr;return pos;}//开始和结束函数Span* begin(){return _head->_next;}Span* end(){return _head;}//判空函数bool Empty(){return begin() == _head;}private://Span*节点Span* _head = nullptr;
public://桶锁std::mutex _mux;
};

 二,CentralCache的整体结构

CentralCache的整体代码如下图所示:

//定义一个CentralCache类,类里面放的是SpanList*对象
//这个CentralCache要定义为单例
class CentralCache
{
public://获取单例static CentralCache* GetInstance(){return &_singleton;}//为threadCache向Central申请内存size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size);Span* GetOneSpan(Spanlist* splist, size_t sz);// 将⼀定数量的对象释放到span中void ReleaseListToSpans(void* start, size_t byte_size);private:CentralCache() {};//不能删除只能私有CentralCache(const CentralCache&) = delete;CentralCache&operator =(CentralCache&) = delete;Spanlist _List[Nspan];static CentralCache _singleton;
};

正如上面的代码所示,CentralCache的实现依赖于它的四个函数和两个成员。

三,CentralCache实现的详细代码

1,成员

CentalCache函数的内部有两个成员。分别是一个spanlist的数组和一个单例对象。前面我已经介绍了spanlist这个结构。那为什么要使用单例呢? 使用单例能够提前生成CentralCache的实例,提高使用效率,方便在别处调用。

 2,函数

//获取单例static CentralCache* GetInstance(){return &_singleton;}//为threadCache向Central申请内存size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size);Span* GetOneSpan(Spanlist* splist, size_t sz);// 将⼀定数量的对象释放到span中void ReleaseListToSpans(void* start, size_t byte_size);

在CentralCache的类中实现了以上四个函数,接下来便详细讲解以上函数的实现。

1, 获取单例对象的指针
static CentralCache* GetInstance(){return &_singleton;}

 这个函数的作用就是获取单例对象的指针。

2, FetchRangeObj函数

具体实现如下:

size_t CentralCache:: FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{//1,先获取大的spanint index = Index(size);CentralCache::_List[index]._mux.lock();Span* span = GetOneSpan(&_List[index], size);//断言下获取到的span和sizeassert(span);assert(size <= MaxBytes);//开始获取:end,start从span的自由链表处开始出发start = span->_freelist;end = span->_freelist;int i = 0;int actualNum = 1;while (i++ < batchNum-1 && NextObj(end) != nullptr){end = NextObj(end);actualNum++;}//截断span->_freelist = NextObj(end);NextObj(end) = nullptr;span->_useCont += actualNum;CentralCache::GetInstance()->_List[index]._mux.unlock();//返回相应的块数return actualNum;
}

这个函数通过调用对齐函数算出传入的大小对应的下标后再调用:GetOneSpan获得一个span并切分通过begin和end带出去交给ThreadCache。

3,GetOneSpan函数实现

具体实现代码如下:

Span* CentralCache::GetOneSpan(Spanlist* splist,size_t sz)
{//先查找范围Span* it = splist->begin();while (it != splist->end()){if (it->_freelist != nullptr){return it;}else{it = it->_next;}}//解锁:为了在下面的调用更加的快捷splist->_mux.unlock();//需要向下申请Span对象PageCache::GetInstance()->_mut.lock();Span* span = PageCache::GetInstance()->NewSpan(PageNum(sz));//从PageCache获取后标记被使用状态span->_used = true;PageCache::GetInstance()->_mut.unlock();//确定得到的span的内存范围,这个_pageid不是从0开始的而是一个地址>>PageShift后得到的char* start = (char*)(span->_pageid << PageShift);int bytes = span->_n << PageShift;char* end = start + bytes;//开始分割Spanspan->_freelist = start;start += sz;void* tail = span->_freelist;while (start < end){NextObj(tail) = start;tail = NextObj(tail);start += sz;}NextObj(tail) = nullptr;//再次加锁:涉及到了splist的操作splist->_mux.lock();//将span头插到spanlist中splist->PushFront(span);//返回spanreturn span;

 这个函数的目的就是得到一个span。当CentralCache中没有sapn时便调用NewSpan函数向下一层的pageCache申请内存。并将span切成一块一块的小内存头插到对应的sanlist中。

 4,ReleaseListToSpans函数实现

具体代码如下:

//ThreadCache释放内存还给CentralCache
void CentralCache::ReleaseListToSpans(void* start, size_t byte_size)
{   //计算是操作那个桶int index = Index(byte_size);_List[index]._mux.lock();//找spanSpan* span = PageCache::GetInstance()->MapObjectToSpan(start);//开始插入到span中while (start){//头插void* Next = NextObj(start);NextObj(start) = span->_freelist;span->_freelist = start;start = Next;//还回来一个就usecount减少一个,当_usecount减少到0就还给PageCachespan->_useCont--;if (span->_useCont == 0){_List[index]._mux.unlock();PageCache::GetInstance()->_mut.lock();//将CentralCache中的内存还给PageCachePageCache::GetInstance()->ReleaseSpanToPage(span);PageCache::GetInstance()->_mut.unlock();_List[index]._mux.lock();}}_List->_mux.unlock();}

这个函数实现的功能是将ThreadCache中的内存还给CentralCache层。                              发生条件是:ThreadCache上的自由链表太长了。

原理:再ThreadCache层调用时ThreadCache层会调用该函数,并传入要归还的内存的首地址。因为每一个地址对应的页号是被设计过的,并且每一个页号和Span都会再PageCache中放入到哈希表中一一对应。所以,通过ThreadCache传入的地址便可以知道这个地址对应的Span,找到这个Span便可以进行头插从而将ThreadCache上过长的自由链表上的内存还给CentralCache。

 四,总结

CentralCache层是高并发内存池的中间层。主要的作用便是为ThreadCache提供内存,提高ThreadCache申请内存的能力。


http://www.mrgr.cn/news/25308.html

相关文章:

  • 揭秘如何利用SCDN守护平台网站安全
  • Java Comparable和Comparator排序接口
  • 如何申请跨境网络专线
  • 计算机领域CCF-C类所有期刊目录,附最新IF和分区
  • mmseqs2进行pdb蛋白质序列聚类分析
  • 适配器模式详解和源码中应用
  • 企业应该如何安全上网,软件防查盗版,企业防盗版
  • 代码随想录冲冲冲 Day43 动态规划Part11
  • SpringBoot与社区团购:构建可扩展的电商平台
  • 视频怎么旋转方向?教你5种视频旋转方向实用方法
  • 外资对冲基金企业岗位:pythonC++开发要求:3-10经验,本科985起,要能说英语可以base上海,新加坡
  • 月考成绩网上在线查询,老师免费发布的查分平台
  • ORACLE 导出/导入表空间
  • 外包干了3天,技术退步明显.......
  • 《ECMAScript 与 JavaScript:差异与共通》
  • 提升效率必备,掌握这些Shell文本处理技能!
  • 怎样训练一个自己的大语言模型?这可能是全网最简单易懂的教程!
  • 拓数派荣登2024年《财富》中国最具社会影响力的创业公司
  • slf4j依赖冲突处理
  • NX1872三维电气布线