在之前的设计中,我没有考虑到当一个线程运行结束时,该线程的线程资源层内可能仍有残余资源的问题(残余内存块数量小于线程资源层的回收标准,没有在用户还回内存块的过程中自动归还给中心资源层),并且ThreadCache本身就是定长内存池ObjectPool开辟出来的空间,ThreadCache本身也应该在线程结束时,释放资源回定长内存池。
解决方法:
通过ThreadCache的析构函数来回收资源,通过智能指针在线程运行结束时自动调用ThreadCache的析构函数。
步骤1:
将原本每个线程独有的TLS ThreadCache指针变量改为unique_ptr智能指针类型,这样当某个线程运行结束时,就会自动调用ThreadCache的析构函数了。当然,由于ThreadCache的资源不是new出来的,而是向定长内存池ObjectPool申请的,因此必须设计unique_ptr的删除器,在删除器内调用ObjectPool的Delete接口。
#pragma once
#include "tool.hpp"
#include "CentreCache.h"
#include <thread>
class ThreadCache
{
public:
~ThreadCache();//线程资源层是需要析构函数的,当某个线程运行完毕后,需要释放该线程的线程资源层资源
void* Allocate(size_t n);
void DeAllocate(void* ptr,size_t n);
void* RequestFromCentralCache(size_t index, size_t alignedSize);//申请空间大于256KB时向中心资源曾申请空间,index为桶的编号,alignedsize为桶里所装内存块的大小
void ThreadCacheListTooLong(FreeList&FList,size_t index,size_t alignedSize);//当线程资源层某个桶里的内存块过多(大于向线程资源层申请空间时一次性允许申请的最多块数时),将桶里目前所有内存块归还给中心资源层(不包括用户还未归还的部分)
private:
FreeList freelists[BUCKETSIZE];
};
//以下是修改部分
class ThreadCachePool
{
public:
ThreadCache* GetThreadCache()
{
return _ThreadCachePool.New();
}
void operator()(ThreadCache* ptr)//删除器
{
_ThreadCachePool.Delete(ptr);
}
private:
ObjectPool<ThreadCache> _ThreadCachePool;
};
//static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;//每个线程独有的变量
static _declspec(thread) std::unique_ptr<ThreadCache,ThreadCachePool> pTLSThreadCache;//每个线程独有的智能指针变量,当线程运行完毕后,自动释放该线程所独占的线程资源层资源,ThreadCachePool在这里为删除器,因为ThreadCache不是new出来的,它来自定长内存池,需要配置删除器
原void* ThisThreadAllocate(size_t n)用户接口内自然也要做相应改动:
#pragma once
#include "ThreadCache.h"
#include "PageCache.h"
//这里函数的作用是对ThreadCache里的接口做封装,使得每个线程都拥有独有的ThreadCache,并使用本文件的函数从自己的ThreadCache里申请空间
void* ThisThreadAllocate(size_t n)
{
if (pTLSThreadCache.get() == nullptr)
{
/*static ObjectPool<ThreadCache> ThreadCachePool;*/
static ThreadCachePool threadCachePool;
static mutex mt;
mt.lock();
/*pTLSThreadCache = ThreadCachePool.New();*/
pTLSThreadCache = std::unique_ptr<ThreadCache,ThreadCachePool>(threadCachePool.GetThreadCache());
mt.unlock();
}
//..............以下内容未做修改故省略
}
步骤2:设计ThreadCache的析构函数
这步很简单,依次将ThreadCache里的每个桶中的所有内存块归还给中心资源层CentreCache即可。
ThreadCache::~ThreadCache()
{
for (int i = 0; i < BUCKETSIZE; i++)//将线程资源层所有桶里的内存块都归还给中心资源层
{
if (!freelists[i].empty())
{
ThreadCacheListTooLong(freelists[i], i, SizeClass::IndexToAlignedSize(i));
}
}
}