Instance(单件)机制原本是让代码执行时只有一个实例,但有的时候又希望每个线程各自能有自己的"单件"相互不影响,处理类似的需求最先想到的就是全局表,然后按线程id或是管理线程的key索引到对应的单件上,取全局表的时候需要加锁。 虽然这样也能实现目的,但是代码看上去很不自然。最近发现还是有更自然的方法能实现这一点,就是 TLS 线程本地存储(Thread Local Storage)
用编译器实现这点比较简单,如果是使用动态创建的方式来实现的单件 则需要申请一个key 来绑定使用这个TLS 单件
---------------------------------------------------------------------------------------------------------------------------
Compiler :
---------------------------------------------------------------------------------------------------------------------------
_declspec (thread) InstanceClassA g_ia;
// 这句告诉给编译器此变量为线程内部使用,每个线程都会copy一份给本地用
---------------------------------------------------------------------------------------------------------------------------
windows 和 linux 下用到的api 稍有不同,但用法差不多,主要是通过申请key 绑定 内存对象 ,释放key 释放内存的 方式进行
WIN API :
---------------------------------------------------------------------------------------------------------------------------
static DWORD InstanceClassA::dwTlsIndex;
// 定义一些DWORD线程全局变量或函数静态变量,准备作为各个线程访问自己的TLS数组的索引变量
InstanceClassA::dwTlsIndex= TLSAlloc();
// 当前线程实际上访问的是这个TLS数组索引变量的线程内的拷贝版本。也就说,不同线程虽然看起来用的是同名的TLS数组索引变量,但实际上各个线程得到的可能是不同DWORD值。其意义在于,每个使用TLS的线程获得了一个DWORD类型的线程局部静态变量作为TLS数组的索引变量。C/C++原本没有直接定义线程局部静态变量的机制,所以在如此大费周折
TlsSetValue(InstanceClassA::dwTlsIndex, new InstanceClassA);
// 动态申请单件内存,然后把指向这块内存区域的指针放入TLS数组相应的槽中,绑定上TlsIndex
InstanceClassA* gp_ia = TlsGetValue(InstanceClassA::dwTlsIndex);
// 用 TlsIndex 取得单件
delete gp_ia ;
// 先释放单件内存
TlsFree(InstanceClassA::dwTlsIndex);
// 然后 释放TLS索引变量 ,如果dwTlsIndex先删除了则很难释放其绑定上的InstanceClassA单件了,在这点上 linux 提供了一个更为保险的方法
----------------------------------------------------------------------------------------------------------------------------
LINUX API :
-----------------------------------------------------------------------------------------------------------------------------
static pthread_key_t InstanceClassA::dwTlsIndex;
// 这步骤同win 一样
static void InstanceClassA::destroy(void *p) { delete (InstanceClassA*)p; }
// 为索引提供一个释放绑定对象的方法
pthread_key_create(&InstanceClassA::dwTlsIndex, &InstanceClassA::destroy);
// 创建出索引并且提供释放方法给索引将会绑定的对象
pthread_setspecific(InstanceClassA::dwTlsIndex, new InstanceClassA);
// 绑定单件到索引
InstanceClassA* gp_ia = pthread_getspecific(InstanceClassA::dwTlsIndex);
// 用 索引 取得单件
pthread_key_delete(InstanceClassA::dwTlsIndex);
// 释放掉索引的同时 ,也会调用 InstanceClassA::destroy 释放掉InstanceClassA
-----------------------------------------------------------------------------------------------------------------------------
static InstanceClassA*GetInstance()
{
InstanceClassA* pm = (InstanceClassA*)pthread_getspecific(InstanceClassA::dwTlsIndex); // or TlsGetValue(InstanceClassA::dwTlsIndex)
if(!pm) pthread_setspecific(InstanceClassA::dwTlsIndex, pm = new InstanceClassA()); // or TlsSetValue(InstanceClassA::dwTlsIndex, new InstanceClassA)
return pm;
}