补充
TLS--thread local storage线程局部存储:
如果把thread cache存储在进程中,多线程共享,1.那就分不清这么多thread cache,分别和哪些线程对应 2.多线程在进程栈创建/访问threadcache 存在临界资源加锁问题。
解决:让每个thread cache存储在对应自己线程的局部存储中。
linux gcc / Windows vs下 TLS
https://zhuanlan.zhihu.com/p/142418922
分为静态/动态 TLS
这里用静态TLS,简单点
////ThreadCache.h
// TLS thread local storage
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
//static使 整个项目可以有多个pTLSThreadCache变量(只在自己的源文件中有效)
//若不加static,当前头文件若被多个源文件包含,会导致项目中有多个相同的全局变量,冲突出错(链接时,在调用/使用处准备寻找定义,发现有多个定义)
////ConcurrentAlloc.h
#pragma once
#include "Common.h"
#include "ThreadCache.h"
static void* ConcurrentAlloc(size_t size)
{
// 通过TLS 每个线程无锁的获取自己的专属的ThreadCache对象
if (pTLSThreadCache == nullptr)
{
pTLSThreadCache = new ThreadCache;
}
cout << std::this_thread::get_id() << ":"<<pTLSThreadCache<<endl;
return pTLSThreadCache->Allocate(size);
}
static void ConcurrentFree(void* ptr, size_t size)
{
assert(pTLSThreadCache);
pTLSThreadCache->Deallocate(ptr, size);
}
Central Cache
central cache也是一个哈希桶结构,他的哈希桶的映射关系跟thread cache是一样的。不同的是他的每个哈希桶位置挂是SpanList链表结构,不过每个映射桶下面的span中的大内存块被按映射关系切成了一个个小内存块对象挂在span的*链表中。
申请内存:
- 当thread cache中没有内存时,就会批量向central cache申请一些内存对象,这里的批量获取对
象的数量使用了类似网络tcp协议拥塞控制的慢开始算法;central cache也有一个哈希映射的
spanlist,spanlist中挂着span,从span中取出对象给thread cache,这个过程是需要加锁的,不
过这里使用的是一个桶锁(一个桶一个锁,只有同时访问同一个桶才会出现资源互斥问题),尽可能提高效率。 - central cache映射的spanlist中所有span的都没有内存以后,则需要向page cache申请一个新的
span对象,拿到span以后将span管理的内存按大小切好作为*链表链接到一起。然后从span
中取对象给thread cache。 - central cache的中挂的span中use_count记录分配了多少个对象出去,分配一个对象给thread
cache,就++use_count
span内的空间是连续的,但各个span之间 空间不一定连续
central cache作用
均衡负载,承上启下
- span是1/多个页的跨度,即包含1/多个页。一个span的大小不固定,具体根据场景来确定。
- 一个span可能分给多个threadcache,用完了/没有一个span有合适的大小后,再向pagecache申请centercache。
- 带头双向循环链表最高效,任意位置插入删除,时间复杂度都是O(1)。
- 整个进程只有一个CenterCache,多个ThreadCache。此时CenterCache适合使用单例模式。
- 不同位的机器物理内存中页个数不同(与地址个数有关)