五、高并发内存池--Thread Cache

news2024/11/20 14:41:40

五、高并发内存池–Thread Cache

5.1 Thread Cache的工作原理

thread cache是哈希桶结构,每个桶是一个按桶位置映射大小的内存块对象的自由链表。每个线程都会有一个thread cache对象,这样每个线程在这里获取对象和释放对象时都是无锁的。
在这里插入图片描述
每一个线程都有一个自己的Thread Cache,即每个线程在向自己的Thread Cache申请内存时是线程安全的,无需加锁,线程之间是不会相互产生影响的,所以大大加快了申请内存的效率。但是每一个线程是在什么时候创建这个自己的Thread Cache的呢?又是怎么创建的呢?
在C++中,_declspec(thread)是一个用于指定线程局部存储( Thread Local Storage,TLS)的关键字。通过将该关键字应用于变量声明,可以确保每个线程都拥有其自己的变量实例,而不是共享同一个变量实例。
具体来说,当变量声明为_declspec(thread)时,每个线程都会有一个独立的变量实例,该变量的生命周期与线程的生命周期一致。每个线程对该变量的访问都是线程安全的,不会与其他线程的访问冲突。
使用_declspec(thread)可以在多线程编程中很方便地创建和使用线程局部变量,它通常用于解决多线程环境下的共享数据问题。请注意,_declspec(thread)是Microsoft Visual C++编译器的特定语法,并不在标准的C++语言规范中定义。其他编译器可能有自己的实现或类似的机制。
也就是说在创建线程之前要先利用_declspec(thread)关键字声明一个静态的变量,这样在创建新的线程需要申请内存的时候就可以通过这个变量去自己专属的Thread Cache中申请内存了,_declspec(thread)声明的变量只会访问自己线程中的变量,不会访问别的线程中的变量,所以通过_declspec(thread)变量的访问是线程安全的,无需加锁。

5.2 ThreadCache.h

//线程缓存
class ThreadCache
{
public:
	//向Thread Cache申请size个字节的内存
	void* Allocate(size_t size);

	//释放size个字节大小的内存到Thread Cache中
	void DeAllocate(void* obj, size_t size);

	//向中心缓存中下标为index位置的哈希桶中获取size个字节大小的内存块
	void* FetchFromCentralCache(size_t index, size_t size);

	//size代表线程申请的一个小对象的大小,自由链表太长就要向中心缓存中还一定量的内存
	void ListTooLong(FreeList& list, size_t size);

private:
	FreeList _freeLists[NFREELIST];//每一个FreeList都是一个自由链表,_freeLists是一个挂满了自由链表的哈希桶数组
};

//线程本地存储变量,_declspec(thread)声明的变量在每一个线程中独享一个
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;

5.3 ThreadCache.cpp


//向Thread Cache申请size个字节的内存
void* ThreadCache::Allocate(size_t size)
{
	//计算向上对齐后的大小
	int alignSize = SizeClass::RoundUp(size);
	//计算所需大小的对象在哪一个哈希桶里面
	int index = SizeClass::Index(alignSize);

	//如果对应下标的哈希桶的自由链表中有内存对象,就直接弹出一个返回
	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();
	}
	else//走到这里说明对应下标的哈希桶的自由链表中没有内存对象,需要找中心缓存拿
	{
		return FetchFromCentralCache(index, alignSize);
	}

}

void ThreadCache::ListTooLong(FreeList& list, size_t size)
{
	void* start = nullptr;
	void* end = nullptr;
	//链表太长,就取出来一部分还给centralcache
	list.PopRange(start, end, list.MaxSize());

	//把start到end这一段链表还给centralcache对应位置的哈希桶中
	//因为CentralCache是所有线程共享一个的,所以把它设计成单例模式
	//即全局只有一个对象较好,CentralCache类提供GetInstance()函数
	//获取类指针调用成员函数
	CentralCache::GetInstance()->ReleaseListToSpans(start, size);

}

//释放内存到Thread Cache中
void ThreadCache::DeAllocate(void* obj, size_t size)
{
	assert(size <= MAX_BYTES);
	assert(obj);

	//计算归还的大小为size的内存块在哪一个哈希桶中
	size_t index = SizeClass::Index(size);

	//把obj插入到对应的哈希桶的自由链表中
	_freeLists[index].Push(obj);

	//如果还回来的小对象太多,导致自由链表太长,就应该还一部分给CentralCache
	if (_freeLists[index].Size() >= _freeLists[index].MaxSize())
	{
		ListTooLong(_freeLists[index], size);
	}
}

//向中心缓存获取内存块
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	//一次向CentralCache申请多个size大小的对象,那么一次要申请多少个呢?
	// 这要根据size的大小计算,如果size很小,那么一次就向CentralCache申请
	// 多一点,如果size很大,那么一次就申请少一点,那么如何计算一次申请多少呢?
	//这里需要使用慢增长的方式控制
	size_t batchNum = min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));
	if (batchNum == _freeLists[index].MaxSize())
	{
		_freeLists[index].MaxSize()++;//慢增长
	}

	//想要向CentralCache获取batchNum个size大小的对象
	void* start = nullptr;
	void* end = nullptr;
	//实际获取到的size大小的对象的数目
	int actualNum = CentralCache::GetInstance()->FetchRangeObj(start, end, batchNum, size);
	assert(actualNum > 0);
	//如果只获取到了一个size大小的对象,那么就直接返回给线程了
	if (actualNum == 1)
	{
		assert(start == end);
		return start;
	}
	else
	{
		//如果获取到了多个内存块,就把start对应的内存块返回给线程
		//其余的都头插到threadcache对应映射位置的哈希桶的自由链表中
		/*NextObj(end) = _freeLists[index]._freeList;
		_freeLists[index]._freeList = start;*/

		_freeLists[index].PushRange(NextObj(start), end, actualNum - 1);

		return start;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/961632.html

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

相关文章

YOLOv5算法改进(11)— 替换主干网络之EfficientNetv2

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。EfficientNetV2是一个网络模型&#xff0c;旨在提供更小的模型和更快的训练速度。它是EfficientNetV1的改进版本。EfficientNetV2通过使用更小的模型参数和采用一种称为Progressive Learning的渐进学习策略来实现这一目标。…

Img标签的src地址自动拼接本地域名(localhost:8080)导致图片不显示问题

摘要&#xff1a;做Vueelement ui项目的时候&#xff0c;发现使用element ui的upload上传图片时&#xff0c;不显示的问题。我项目的图片是上传到七牛云&#xff0c;长传成功后返回存储在七牛云中的地址。后面发现是因为返回的地址是外部地址&#xff0c;需要完整的URL&#xf…

LVS 实现四层负载均衡项目实战--NAT模式

一、原理 就是把客户端发来的数据包的IP头的目的地址&#xff0c;在负载均衡器上换成其中一台RS的IP地址&#xff0c;转发至此RS来处理,RS处理完成后把数据交给经过负载均衡器,负载均衡器再把数据包的源IP地址改为自己的VIP&#xff0c;将目的地址改为客户端IP地址即可&#x…

【Linux系列】vmware虚拟机网络配置详解

非原创 原文地址[1] 首发博客地址[2] 系列文章地址[3] vmware 为我们提供了三种网络工作模式&#xff0c;它们分别是&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09;。 打开…

Linux:tomcat (源码包安装)(官网下载-安装-启动-配置-等等等-----从入门到入土)

介绍 Apache Tomcat软件是一个开源实现 Jakarta Servlet、Jakarta Server Pages、Jakarta Expression Language、Jakarta WebSocket、Jakarta Annotations 和 Jakarta Authentication 规范。 这些规范是Jakarta EE平台的一部分。 Apache Tomcat软件是在开放和参与式中开发的。 …

大数据时代下的数据安全防护

随着大数据时代的来临&#xff0c;数据安全防护成为了一个重要的问题。在大数据时代&#xff0c;数据的规模和价值都得到了极大的提升&#xff0c;因此数据安全的重要性也变得越来越突出。本文将从数据加密、访问控制、网络安全和人员管理四个方面来介绍大数据时代下的数据安全…

Ubuntu22.04上下左右全方位美化教程

Ubuntu22.04上下左右全方位美化教程 以Plank替代Dock甲板安装使用优化除了Plank之外还有Ubuntu-Launchpad可以替代Dock Tweak-Tool配置主题Theme的配置下载解压配置 Icon文件夹显示风格的配置Cursors鼠标风格优化Background背景、Lock锁屏以及登陆页面的更换过渡动画配置安装 E…

excel绘制直方图

Excel 2016直方图使用指南 excel绘制各种曲线十分方便&#xff0c;可以通过代码将计算的数据输出到excel里面&#xff0c;然后通过excel的插入标签&#xff0c;绘制各种需要的曲线。 对于直方图&#xff0c;横坐标是分布区间&#xff0c;纵坐标是这个区间内数值的频数&#x…

Nginx 部署 配置

一.概述 什么是nginx? Nginx (engine x) 是一款轻量级的Web 服务器 、反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。 什么是反向代理&#xff1f; 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求…

【vue2第七章】vue的四个生命周期与八个钩子函数

vue的四个生命周期与八个钩子函数 Vue的四个生命周期有&#xff1a;创建&#xff08;creation&#xff09;、挂载&#xff08;mounting&#xff09;、更新&#xff08;updating&#xff09;和销毁&#xff08;destroying&#xff09;。 钩子函数是什么&#xff1a; vue生命周…

React动态添加标签组件

背景 在前端开发的过程中,一些表单的输入经常需要输入多个内容,如果采用一个输入框逗号分隔的方式,展示起来不是很清晰,一般需要采用标签的方式 需求 可以指定空状态时的标题设置标签颜色每个标签的最大长度(字符数)接口传递的时候的分隔标记(是用逗号,还是其他)直接处理表单,不…

基于OpenCV+Keras+tensorflow 实现的变电站作业管控平台源代码。含人脸识别考勤,移动目标跟踪,越线检测,安全措施检测,姿态识别等功能

#综述 使用该作业现场安全生产智能管控平台来实现变电站的安全生产的智能化管理&#xff0c;通过人脸识别功能进行人员的考勤&#xff1b; 通过人员、车辆的检测和识别来实现变电站的智能化管理&#xff1b;通过安全行为识别和安全区域报警功能来实现对变电站内人员和设备安全的…

线程同步与互斥

目录 前言&#xff1a;基于多线程不安全并行抢票 一、线程互斥锁 mutex 1.1 加锁解锁处理多线程并发 1.2 如何看待锁 1.3 如何理解加锁解锁的本质 1.4 CRAII方格设计封装锁 前言&#xff1a;基于线程安全的不合理竞争资源 二、线程同步 1.1 线程同步处理抢票 1.2 如何…

分库分表相关知识

文章目录 一、为什么要分库分表1.1 什么是分库1.2 什么是分表1.3 为什么要分库1.3.1 磁盘存储1.3.2 并发连接支撑 1.4 为什么要分表 二、分库分表解决方案2.1 垂直&#xff08;纵向&#xff09;切分2.1.1 垂直切分优点2.1.2 垂直切分缺点 2.2 水平&#xff08;横向&#xff09;…

video 视频编解码一些debug方法

文章目录 一、通过命令去获取一些数据1.2 确定我们xml配置文件: 二、查看我们芯片支持的编码能力三、通过log去获取信息 这个文章的主要内容是为了后期性能方面的debug, 设计到前期的bringup则没有 一、通过命令去获取一些数据 获取媒体相关的参数&#xff1a; # getprop |…

失效的访问控制

文章目录 渗透测试漏洞原理失效的访问控制1. 失效的访问控制1.1 OWASP TOP 101.1.1 A5:2017-Broken Access Control1.1.2 A01:2021-Broken Access Control 1.2 失效的访问控制类别1.2.1 水平越权1.2.2 垂直越权 1.3 攻防案例1.3.1 DVWA越权 1.4 相关漏洞1.4.1 目录遍历1.4.2 未…

泥石流山体滑坡监控视觉识别检测算法

泥石流山体滑坡监控视觉识别检测算法通过yolov8python深度学习框架模型&#xff0c;泥石流山体滑坡监控视觉识别检测算法识别到泥石流及山体滑坡灾害事件的发生&#xff0c;算法会立即进行图像抓拍&#xff0c;并及时进行预警。Yolo的源码是用C实现的&#xff0c;但是好在Githu…

解决Echarts中双坐标轴分割错位问题

1、处理函数 /*** Description 刻度最大值* date 2023-08-30* param {any} isNaN(maxValue/1* returns {any}*/ export const getYAxisMax (maxValue): number > {if (isNaN(maxValue / 1) || maxValue / 1 < 10) {return 10;}const max: any Math.ceil(maxValue) ;c…

Vue框架--Vue中el和data的两种写法

data与el的2种写法 1.el有2种写法 (1).new Vue时候配置el属性。 (2).先创建Vue实例&#xff0c;随后再通过vm.$mount(#root)指定el的值。 2.data有2种写法 (1).对象式 (2).函数式 如何选择&#xff1a;目前哪种写法都可以&#xff0c;以后学习到组件时&#xff…

Redis-Cluster集群操作--添加节点

一、环境部署 部署好Redis-Cluster集群&#xff0c;参考上个本人的博客&#xff1a;Redis-Cluster集群的部署&#xff08;详细步骤&#xff09;_是胡也是福的博客-CSDN博客 新准备一台机器&#xff0c;修改主机名&#xff0c;关闭防火墙和selinux&#xff0c;参考&#xff1a…