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

news2024/11/16 1:16:11

目录

一,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,先获取大的span
	int index = Index(size);
	CentralCache::_List[index]._mux.lock();
	Span* span = GetOneSpan(&_List[index], size);
	//断言下获取到的span和size
	assert(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;

	//开始分割Span
	span->_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);

	//返回span
	return 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();
	//找span
	Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
	//开始插入到span中
	while (start)
	{
		//头插
		void* Next = NextObj(start);
		NextObj(start) = span->_freelist;
		span->_freelist = start;
		start = Next;
		//还回来一个就usecount减少一个,当_usecount减少到0就还给PageCache
		span->_useCont--;
		if (span->_useCont == 0)
		{
			_List[index]._mux.unlock();
			PageCache::GetInstance()->_mut.lock();
			//将CentralCache中的内存还给PageCache
			PageCache::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.coloradmin.cn/o/2130339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货分享丨智造底座——AI算力池化的必要与实践

为了帮助工业企业更好地了解数据存储及管理的应用趋势&#xff0c;e-works在线学院于2022年9月23日14:00举办了以“工业数据存储及管理”为主题的线上研讨会。 趋动科技售前工程师Johny Hong受邀作主题分享&#xff0c;并与线上几千观众共同探讨了建设智造底座——AI算力池化的…

【学习笔记】手写 Tomcat 三

目录 多线程处理 1. 创建线程 thread 2. 使用 start() 启动线程 3. 启动服务端 响应动态资源 JDBC 创建 lib 文件夹&#xff0c;存放第三方 jar包 JDBC 的7个步骤 定义响应动态资源的方法 DTO 数据传输对象 再创建一个响应方法 测试 作业 1. 客户端发送 -1 报错…

ES-Search API

一、Search API的分类 URI Search 在url中传入查询参数进行查询&#xff0c;Request Body Search 使用es提供的&#xff0c;基于json的更加完备的Query Domain Specific Language(DSL) 语法范围/_search集群上所有的索引/index1/_search查询索引名为index1的索引/index1,inde…

828华为云征文|基于华为云Flexus云服务器X部署Minio服务

文章目录 ❀前言❀Minio简介❀部署环境准备❀yum环境配置❀安装docker❀获取镜像❀创建挂载目录❀启动容器❀查看容器状态❀安全组开放❀浏览器访问❀总结 ❀前言 大家好&#xff0c;我是早九晚十二。 近期华为云推出了最新的华为云Flexus云服务器X&#xff0c;这款云主机在算…

开发模式和环境搭建

后台用到的知识 Validatation&#xff1a;参数校验Mybatis&#xff1a;数据库的操作Redis&#xff1a;缓存Junit&#xff1a;单元测试项目部署 开发模式 使用接口文档对应路径和请求方式、请求参数、响应数据作详细说明。 环境搭建 执行big_event.sql脚本准备数据库表 创建…

240416 初始化列表 构造与隐式类型转换 static成员 友元 内部类

一、初始化列表 1、认识 【P】Stack不具备默认构造&#xff0c;MyQueue也无法生成默认构造 【S】引入初始化列表 MyQueue(int n):_pushst(n),_popst(n),_size(0) {}初始化列表本质上可以理解为每个对象中成员定义的地方 所有成员既可以在初始化列表初始化&#xff0c;也可以…

1. geoserver 安装

1. 下载 geoserver官网 以window为例 选择稳定版本 不同平台需要下载不同安装包 如果需要发布瓦片图层&#xff0c;需要下载扩展插件 前提需要有java环境&#xff0c;根据提示默认安装&#xff0c;需要安装在不同位置选择自己的文件夹即可 2. 使用 扩展插件解压之后的所…

【油猴脚本】编写一个简单的油猴(Tampermonkey)脚本,在网页中加入一个表格

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享【油猴脚本】编写一个简单的油猴&#xff08;Tampermonkey&#xff09;脚本&#xff0c;在网页中加入一个表格&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xf…

2024年最新老薛主机优惠码

老薛主机优惠码是其推出的一种优惠促销方式&#xff0c;大家在新购或续费老薛主机相关产品时&#xff0c;填写使用优惠码可以享受专属折扣。 一、老薛主机优惠码 1、终身七折优惠码&#xff1a;u70 2、新购七折优惠码&#xff1a;ABC 二、优惠码适用范围 老薛主机的优惠码通…

《使用 LangChain 进行大模型应用开发》学习笔记(三)

前言 本文是 Harrison Chase &#xff08;LangChain 创建者&#xff09;和吴恩达&#xff08;Andrew Ng&#xff09;的视频课程《LangChain for LLM Application Development》&#xff08;使用 LangChain 进行大模型应用开发&#xff09;的学习笔记。由于原课程为全英文视频课…

Keil MDK报错:Browse information of one or more files is not available----解决方法:

Keil MDK报错&#xff1a;Browse information of one or more files is not available----解决方法&#xff1a; 问题描述 最近在项目中遇到这样一个问题&#xff1a;拷贝过来添加到工程的.c文件在编译时报如下错误&#xff1a; 解决方案&#xff1a; 总结以下一些解决办法&…

PCL 读取和保存点云

目录 一、概述 1.1原理 1.2实现步骤 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 1.1原理 PCL (Point Cloud Library) 是…

开源免费的工贸一体行业ERP管理系统

引言 在当今数字化浪潮汹涌澎湃的时代&#xff0c;中小企业面临着前所未有的挑战与机遇。如何实现数字化转型发展&#xff0c;成为了众多中小企业主心头的大事。 据相关数据显示&#xff0c;目前我国中小企业数量已经超过了 4000 万户&#xff0c;然而成功实现数字化转型的比例…

单向链表之创建,插入,输出(下)

文章目录 &#x1f34a;自我介绍&#x1f34a;插入&#xff08;下&#xff09;尾插法有序插入&#xff1a;按照指定顺序插入 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 H…

Arduino 2线串行 通信 驱动 LCD 12864

上效果图片 源代码 Arduino 328p /****************************************************************** 2线 LCD 12864 (屏幕主控芯片 ST7920)******************************************************************//***********************端口/引脚定义区域************…

Golang | Leetcode Golang题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; func readBinaryWatch(turnedOn int) (ans []string) {for i : 0; i < 1024; i {h, m : i>>6, i&63 // 用位运算取出高 4 位和低 6 位if h < 12 && m < 60 && bits.OnesCount(uint(i)) turnedOn {ans …

WPF创建不规则窗体时WebBrowser控件不显示的问题

最近有小伙伴需要在不规则窗体上放置WebBrowser控件&#xff0c;因为设置了WindowStyle"None" 和 AllowsTransparency"True"。 导致WebBrowser控件不显示。 界面代码如下所示&#xff1a; 1 <Window x:Class"WebBrowserDemo.MainWindow" …

【CTF Web】BUUCTF BUU UPLOAD COURSE 1 Writeup(文件上传+PHP+文件包含漏洞)

BUU UPLOAD COURSE 1 1 上课用~ 点击启动靶机。 解法 疑似存在文件包含漏洞。 http://15a5666e-1796-4f76-b892-0b69cf97df8e.node5.buuoj.cn:81/index.php?fileupload.php查看网页源代码。判断是后端检查。 <!DOCTYPE html> <html lang"zh-cn"> &…

区块链Dapp开发:质押挖矿的各模式开发详解

质押挖矿是区块链去中心化金融&#xff08;DeFi&#xff09;生态系统中的核心部分&#xff0c;吸引了大量用户参与。随着Dapp&#xff08;去中心化应用&#xff09;的普及&#xff0c;质押挖矿的模式多样化发展&#xff0c;不同模式各有其优点和风险。本文将介绍质押挖矿的基本…

SpringBoot快速接入OpenAI大模型(JDK8)

使用AI4J快速接入OpenAI大模型 本博文给大家介绍一下如何使用AI4J快速接入OpenAI大模型&#xff0c;并且如何实现流式与非流式的输出&#xff0c;以及对函数调用的使用。 介绍 由于SpringAI需要使用JDK17和Spring Boot3&#xff0c;但是目前很多应用依旧使用的JDK8版本&…