关于高并发内存池的项目的博客CSDN上已经有很多,其中个人认为最优秀的是龙哥写的【项目设计】高并发内存池,强烈建议阅读。
此篇博客会在龙哥博客基础上进行总结和拓展,包括如何进行修改能够兼容Windows和Linux两种操作系统、哪些地方的细节需要注意等等。
项目源代码: C++实现的高并发内存池项目 。直接复制源代码即可在Windows或Linux中运行。
实现Linux与Windows跨系统运行
原来的项目主要是在Windows下运行,如果要兼容Linux,首先需要将Linux下申请和释放内存的接口补在条件编译那里:
另外由于Linux下并没有Windows自带的TLS接口,需要将TLS改成Linux下的__thread
,在使用TLS时直接使用条件编译即可:
Linux下并不支持atomic的定义时就初始化,所以要先定义然后再赋值。
Linux与Windowsclock()
函数的返回值为硬件滴答数,与编译器有关。
在Windows的VS下每1000个滴答是一秒,在Linux的g++下为1000000个滴答为一秒。所以Linux下的返回值是Windows下的一千倍。
如果想要计算多少秒,需要除以CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,也就是硬件滴答数。
解决完上述问题只需要编写makefile即可运行程序:
一些需要注意的问题
定长内存池加锁以及大小的问题
之前的定长内存池是没有锁的,所以在多线程使用定长内存池申请pTLSThreadCache
时就会出现问题:
这里有两种解决办法,一种是龙哥那种在申请前加锁。
另一种则是在定长内存池内部加锁,在申请和释放内存时进行加锁和解锁:
这里更加推荐第二种,因为这样能够让定长内存池更加安全,减少外部加锁的繁琐操作。
另外定长内存池有两个需要注意的地方,一个是obj的大小必须要比一个指针的空间大,否则无法形成链表;第二个是大块空间不够时,我们需要重新开辟一大块空间,但这新开辟的大块空间可能还是不够obj的大小(比如在64位时使用3层基数树那里需要开辟第一层节点时,就会出现这种情况,因为第一层节点需要开辟1M多的空间,但为了避免浪费,定长内存池我们不会一次性开那么大的大块内存),所以我们需要进行一次判断,如果obj的大小比大块空间还大的话,则需要开辟obj大小的空间。
线程结束存在内存泄漏的问题
虽然我们已经有了ThreadCache将内存还给CentralCache的接口,但归还有一个前提,那就是ThreadCache哈希桶中链表的长度必须要到一定的长度才会归还。
这也就意味着当一个线程结束,即使程序员已经调用了ConcurrentFree来归还内存,但归还只是还给ThreadCache,ThreadCache并不一定会还给CentralCache。
所以ThreadCache中会存在内存泄漏的问题。
这里的解决办法有很多,最简单的方式就是写一个接口手动归还ThreadCache中的内存,其本质就是对ReleaseListToSpans
接口的复用,另外还需要将ThreadCache中的哈希桶清空(不清空也没事)。
当然手动调用毕竟不友好,会出现忘记调用的情况,这也是后续优化的点,让其随线程结束自动调用。