目录前言1.Common.hpp2.UnitTest.cpp3. ConCurrentAlloc.h4. ThreadCache.h5.ThreadCache.cpp设计总结前言前面一个设计了一个定长内存池经过与传统的内存申请方式对比初步实现了一个可承受频繁申请与释放的高性能的内存池这是我们首先要看到的一个内存池被设计出来实现的可观效果。从这里开始我们将一步步的对高并发内存池进行由浅入深的设计本节我们来设计ThreadCache。threadcache线程缓存是每个线程独有的⽤于小于256KB的内存的分配线程从这里申请内存 不需要加锁每个线程独享⼀个cache这也就是这个并发线程池高效的地方1.Common.hpp我们将公共代码独立合并到这个文件当中方便调用#pragma once #include iostream #include vector #include ctime #include new #include assert.h #include thread static const size_t MAX_SIZE 256 * 1024; static const size_t NFREE_LIST 208; static void* Next(void* obj) { return *(void**)obj; } class FreeList { public: void Push(void* obj) { Next(obj) _freelist_head; _freelist_head obj; } void* Pop() { void* obj _freelist_head; _freelist_head Next(obj); return obj; } bool Empty() { return _freelist_head nullptr; } private: void* _freelist_head; }; class SizeClass { public: // 整体控制在最多10%的浪费 // [1,128] 8 byte对齐 freelist[0,16) //16个桶 // [1281,1024] 16byte 对齐 freelist[16,72) // [10241,8*1024] 128 byte对齐 freelist[72,128) // [8*10241,64*1024] 1024 byte对齐 freelist[128,184) // [64*10241,256*1024] 8*1024 byte对齐 freelist[184,208) static inline size_t _RoundUp(size_t size, size_t alignNum) { return ((size alignNum - 1) ~(alignNum - 1)); } //向上对齐 7-8 8-8 9-16 static inline size_t RoundUp(size_t size) { if (size 128) { return _RoundUp(size, 8); } else if (size 1024) { return _RoundUp(size, 16); } else if (size 8 * 1024) { return _RoundUp(size, 128); } else if (size 64 * 1024) { return _RoundUp(size, 1024); } else if (size 256 * 1024) { return _RoundUp(size, 8 * 1024); } else { assert(false); return -1; } } // 前16个桶每个桶挂一个链链上的每一块内存大小都一样往后每个桶按照8字节的步长增加 // 第一个桶的内存都是8字节第二个都是16字节...最后一个都是128字节 static inline size_t _Index(size_t bytes, size_t align_shift) { return ((bytes (1 align_shift) - 1) align_shift) - 1; } // 计划映射到哪一个自由链表桶 static inline size_t Index(size_t bytes) { //每个区间有多少个链 static int group_array[4] { 16, 56, 56, 56 }; if (bytes 128) { return _Index(bytes, 3); } else if (bytes 1024) { return _Index(bytes - 128, 4) group_array[0]; } else if (bytes 8 * 1024) { return _Index(bytes - 1024, 7) group_array[1] group_array[0]; } else if (bytes 64 * 1024) { return _Index(bytes - 8 * 1024, 10) group_array[2] group_array[1] group_array[0]; } else if (bytes 256 * 1024) { return _Index(bytes - 64 * 1024, 13) group_array[3] group_array[2] group_array[1] group_array[0]; } else { assert(false); } return -1; } private: };2.UnitTest.cpp我们从用户的角度来一步步探查当前的内存池完成进度#include ObjectPool.hpp #include ConcurrentAlloc.h void Alloc1() { for (int i 1; i 5; i) { void* ptr ConcurrentAlloc(6); } } void Alloc2() { for (int i 1; i 5; i) { void* ptr ConcurrentAlloc(7); } } void TLSTest() { std::thread t1(Alloc1); std::thread t2(Alloc2); t1.join(); t2.join(); } int main() { //TestObjectPool(); TLSTest(); return 0; }这里分别创建了两个线程来执行两个不同的Alloc函数里面调用的ConcurrentAlloc很明显是为ptr申请内存的我们来看看这个函数3. ConCurrentAlloc.h#pragma once #include ThreadCache.h #include Common.hpp static void* ConcurrentAlloc(size_t size) { // 通过TLS每个线程无锁地获取自己的专属ThreadCache对象 if (pTLSThreadCache nullptr) { //懒加载思想需要用到这个线程的时候再申请该线程的ThreadCache空间 pTLSThreadCache new ThreadCache; } // 向当前线程的ThreadCache中申请内存 return pTLSThreadCache-Allocate(size); } // 传size是要确定将不同大小的内存块精确的还给对应桶 static void ConcurrentFree(void* ptr, size_t size) { assert(pTLSThreadCache); pTLSThreadCache-Deallocate(ptr, size); }从这里就知道内存是通过类对象pTLSThreadCache来申请和释放的4. ThreadCache.h#pragma once #include Common.hpp class ThreadCache { public: void* Allocate(size_t size); void* Deallocate(void* ptr, size_t size); void* FetchFromCentralCache(size_t index, size_t alignSize); private: // 每个线程都有独自的一批存放内存的链表桶 FreeList _freelists[NFREE_LIST]; }; // TLS Thread Local Storage //线程局部存储让每一个线程都有独立的存储空间避免了加锁的开销 static _declspec(thread) ThreadCache* pTLSThreadCache nullptr;最后一句代码实现了让每一个线程都拥有一个独立的ThreadCache类它们各自管理各自内存互不干扰因此前面不同线程申请内存的时候并不需要加锁。我们来具体看看ThreadCache类中函数的实现5.ThreadCache.cpp#include ThreadCache.h void* ThreadCache::FetchFromCentralCache(size_t index, size_t alignSize) { // ... return nullptr; } void* ThreadCache::Allocate(size_t size) { assert(size MAX_SIZE); // 1. 申请字节数向上对齐 size_t alignSize SizeClass::RoundUp(size); // 2. 桶下标 size_t index SizeClass::Index(size); if (!_freelists[index].Empty()) { return _freelists[index].Pop(); } else { return FetchFromCentralCache(index, alignSize); } } void* ThreadCache::Deallocate(void* ptr, size_t size) { assert(ptr); assert(size MAX_SIZE); size_t index SizeClass::Index(size); _freelists[index].Push(ptr); }根据申请的字节数先向上对齐然后再根据字节数确定桶下标也就是从哪个桶申请如果该桶没有剩余内存了就需要向中心缓存CentralCache中申请内存但目前尚未设计到中心缓存结构因此函数FetchFromCentralCache暂不实现。回收内存机制需要对目标内存块大小进行判定确认回收到哪个桶再Push到该桶中每个桶都是存放大小一致内存块的链表该操作就是将内存块头插入链表中。设计总结给每一个线程独立申请一块ThreadCache内存每一个ThreadCache就是FreeList _freelists[NFREE_LIST]存放208个链表各个链表之间管理的内存块大小不同但是同一个链表内的所有内存块大小是一致的。用户通过不同线程调用ConcurrentAlloc和 ConcurrentFree申请和释放内存每个线程都有一个独立的ThreadCache类对象pTLSThreadCache各个线程之间申请内存互不影响因为pTLSThreadCache是线程独立资源因此不加锁也可以各自调用Allocate和Deallocate实现并发申请和释放资源提升内存申请释放效率。附加理解每个线程都有一个独立的ThreadCache对象也就是pTLSThreadCache那个但是不是一开始创建线程就申请如果不用就太浪费了而是用到了这个线程才会申请出来这里引入了“懒加载”思想来进行设计。然后根据需要申请的空间向上对齐 桶下标定位之后向该桶链表申请空间如果该桶没有空间了。再通过FetchFromCentralCache向中心缓存申请由此引入中心缓存的结构设计这个由下一篇详细介绍目前项目到这里大概就是这么个思路后续敬请期待~~
企业数字化 ERP 产品动态
相关推荐
一次生成还是多轮修补:两种工作流的真实产出效率对比 一次生成还是多轮修补:两种工作流的真实产出效率对比
用 4B 小模型生成一个能跑的单文件 Three.js 场景,真正的分岔点不在提示词写得多漂亮,而在第一次生成之后你做什么选择:重新掷骰子,还是拿着上一版代码打补丁。前… · 2026/11/3 2:54:28
新手也能上手!2026年靠谱AI论文工具榜单,AI工具一键写高质论文 2026 年实测 10 款主流 AI 论文工具,千笔AI以全流程覆盖 语义级降重 免费查重领跑综合榜;ThouPen 稳坐留学生毕业全流程工具头把交椅;免费工具中DeepSeek Scholar、豆包学术版表现亮眼,30 分钟即可生成万字高质量初稿࿰… · 2026/11/3 2:54:28
都说“数据是资产“,到底什么意思?我用三个问题判断你那堆数据算不算 上周跟一个老板吃饭,他说了一句特别有代表性的话:“我公司最值钱的就是这些客户资料,记了三年了。”
我随口问了句:这三年里,哪个客户最赚钱?哪个客户在慢慢不下单了?
他答不上来。
不是他不聪明… · 2026/11/3 2:53:28
StatusServer:分布式系统健康监控的核心架构与实践 1. StatusServer:现代分布式系统的健康监控中枢在分布式架构成为主流的今天,服务健康监控已从"可有可无"变成了"生死攸关"的基础设施。三年前我们某个核心服务的一次雪崩事故让我深刻认识到:当30个微服务节点中有3个出现… · 2026/11/3 3:29:34
SAP Fiori扩展部署:BAS与gCTS集成实战指南 1. 这不是“加个按钮”那么简单:Fiori扩展部署的本质是ABAP系统能力的再定义你看到“SAP Fiori 扩展部署到 ABAP 环境”这个标题,第一反应是不是——不就是改个UI5代码、打个包、传到后台?我试过太多次了,这种想法在2024年已经成了… · 2026/11/3 3:29:34
2026年AI发展:从规模竞赛到数据智能的转型 1. 2026年AI发展的关键转折点2026年很可能成为人工智能发展史上的一个重要分水岭。过去十年间,AI领域最显著的竞争焦点就是模型规模的不断膨胀——从最初的百万参数到如今的万亿参数,各大科技公司都在比拼谁能训练出更大的模型。但这种"越大越好&qu… · 2026/11/3 3:29:34
虚拟展览VR/AR开发:从帧率预算到交互验收的工程实践 简介:面向VR/AR初学者、独立开发者及教学备课人员,这份文档以“创建虚拟展览体验”为主线,给出可扩展的开发脚本案例与流程框架,帮助读者快速理清从创意到可发布应用的实现路径。内容围绕项目初始化与环境配置、3D模型导入与场景布… · 2026/11/3 3:29:34
C语言期末305题题库查漏:语法数组指针易错点复习地图 简介:这份详解版C语言期末题库面向高校理工科学生与备考C语言期末考试的读者,聚焦选择题、填空题、判断题、读程序写结果和压轴编码等常见题型,帮助在有限时间内梳理函数、标识符、三种控制结构、数据类型、运算符、条件表达式及标准库函数等… · 2026/11/3 3:29:34
进程同步四大经典模型:死锁、信号量与并发故障排查 凌晨两点被叫起来救一个"服务假死"的场,日志停在某一行不动,进程活着,CPU 几乎为零,所有请求都超时。抓线程栈一看,两个工作线程一个卡在入队、一个卡在出队,各自握着一把锁,等着对方… · 2026/11/3 3:28:34
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 4:42:45
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 5:17:18