设计自己的线程局部存储

设计自己的线程局部存储

(1)自动管理它所保存的指针所指向的内存单元的分配和释放。这样做,一方面大大方
便了用户使用,另一方面,在一个线程不使用线程局部变量的情况下,管理系统可以决定不为
这个线程分配内存,从而节省内存空间。

(2)允许用户申请使用任意多个TLS索引。Microsoft确保每个进程的位数组中至少有TLS_MINIMUM_AVAILABLE个位标志是可用的。在WinNTh文件中这个值被定义为64,Windows 2000又做了扩展,使至少1000个标志可用。

新的TLS主要由4个类组成

CSimpleList类负责实现简单的链表功能,把各线程私有数据连在一起,以便能够释放它们占用的内存;

CNoTrackObject类重载了new和delete操作符,负责为线程私有数据分配内存空间;

CThreadSlotData类是整个系统的核心,它负责分配索引和存取线程私有数据;

CThreadLocal是最终提供给用户使用的类模板,它负责为用户提供友好的接口函数。

1622167447633

线程的私有数据是自己自定义的结构或类,需要继承CNoTrackObject类

一个存放线程私有数据的数据结构CThreadData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
///////////////////////////////////////////////////////
// _AFXTLS_.H文件



#ifndef __AFXTLS_H__ // _AFXTLS_.H 文件
#define __AFXTLS_H__

#include <windows.h>
#include <stddef.h>


class CNoTrackObject;

////////////////////////////////////////////////
// CSimpleList

class CSimpleList
{
public:
//构造函数
CSimpleList(int nNextOffset = 0);
//设置数据结构中pNext成员的偏移量
void Construct(int nNextOffset);

// 提供给用户的接口函数(Operations),用于添加、删除和遍历节点
BOOL IsEmpty() const; //判断是否为空链表
void AddHead(void* p); //添加节点
void RemoveAll(); //删除所有结点
void* GetHead() const; //获取头结点
void* GetNext(void* p) const; //获取下一个结点
BOOL Remove(void* p); //删除结点

// 为实现接口函数所需的成员(Implementation)
void* m_pHead; // 链表中第一个元素的地址
size_t m_nNextOffset; // 数据结构中pNext成员的偏移量
void** GetNextPtr(void* p) const; //获取pNext的偏移地址
};

// 类的内联函数
inline CSimpleList::CSimpleList(int nNextOffset)
{ m_pHead = NULL; m_nNextOffset = nNextOffset; }
//设置数据结构中pNext成员的偏移量
inline void CSimpleList::Construct(int nNextOffset)
{ m_nNextOffset = nNextOffset; }
//判断链表为空,返回NULL
inline BOOL CSimpleList::IsEmpty() const
{ return m_pHead == NULL; }
//移除所有结点
inline void CSimpleList::RemoveAll()
{ m_pHead = NULL; }
//获取头节点
inline void* CSimpleList::GetHead() const
{ return m_pHead; }
//获取下一个结点的地址
inline void* CSimpleList::GetNext(void* preElement) const
{ return *GetNextPtr(preElement); }
//获取数据结构中pNext成员的偏移量
inline void** CSimpleList::GetNextPtr(void* p) const
{ return (void**)((BYTE*)p + m_nNextOffset); }


template<class TYPE>
class CTypedSimpleList : public CSimpleList
{
public:
CTypedSimpleList(int nNextOffset = 0)
: CSimpleList(nNextOffset) { }
void AddHead(TYPE p)
{ CSimpleList::AddHead((void*)p); }
TYPE GetHead()
{ return (TYPE)CSimpleList::GetHead(); }
TYPE GetNext(TYPE p)
{ return (TYPE)CSimpleList::GetNext(p); }
BOOL Remove(TYPE p)
{ return CSimpleList::Remove(p); }
operator TYPE()
{ return (TYPE)CSimpleList::GetHead(); }
};


////////////////////////////////////////////////
// CNoTrackObject
class CNoTrackObject
{
public:
void* operator new(size_t nSize);
void operator delete(void*);
virtual ~CNoTrackObject() { }
};

/////////////////////////////////////////////////
// CThreadSlotData - 管理我们自己的线程局部存储

// warning C4291: no matching operator delete found
#pragma warning(disable : 4291)

struct CSlotData;
struct CThreadData;

class CThreadSlotData
{
public:
CThreadSlotData();

// 提供给用户的接口函数(Operations)
int AllocSlot();
void FreeSlot(int nSlot);
void* GetThreadValue(int nSlot);
void SetValue(int nSlot, void* pValue);
void DeleteValues(HINSTANCE hInst, BOOL bAll = FALSE);

// 类的实现代码(Implementations)
DWORD m_tlsIndex; // 用来访问系统提供的线程局部存储

int m_nAlloc; // m_pSlotData所指向数组的大小
int m_nRover; // 为了快速找到一个空闲的槽而设定的值
int m_nMax; // CThreadData结构中pData指向的数组的大小
CSlotData* m_pSlotData; // 标识每个槽状态的全局数组的首地址
CTypedSimpleList<CThreadData*> m_list; // CThreadData结构的列表
CRITICAL_SECTION m_cs;

void* operator new(size_t, void* p)
{ return p; }
void DeleteValues(CThreadData* pData, HINSTANCE hInst);
~CThreadSlotData();
};


///////////////////////////////////////////////

class CThreadLocalObject
{
public:
// 属性成员(Attributes),用于取得保存在线程局部的变量中的指针
CNoTrackObject* GetData(CNoTrackObject* (*pfnCreateObject)());
CNoTrackObject* GetDataNA();

// 具体实现(Implementation)
DWORD m_nSlot;
~CThreadLocalObject();
};


template<class TYPE>
class CThreadLocal : public CThreadLocalObject
{
// 属性成员(Attributes)
public:
TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
return pData;
}
TYPE* GetDataNA()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
operator TYPE*()
{ return GetData(); }
TYPE* operator->()
{ return GetData(); }

// 具体实现(Implementation)
public:
static CNoTrackObject* CreateObject()
{ return new TYPE; }
};


#define THREAD_LOCAL(cla.ss_name, ident_name) \
CThreadLocal<class_name> ident_name;
#define EXTERN_THREAD_LOCAL(class_name, ident_name) \
extern THREAD_LOCAL(class_name, ident_name)


#endif // __AFXTLS_H__


/*


2.2.4


#ifndef __AFXTLS_H__ // _AFXTLS_.H 文件
#define __AFXTLS_H__

#include <windows.h>

class CThreadLocalObject
{
public:
CThreadLocalObject();

// 属性成员(Attributes),用于取得保存在线程局部的变量中的指针
LPVOID GetData(LPVOID (*pfnCreateObject)());
LPVOID GetDataNA();

// 执行体(Implementation),实现类接口所需的成员
DWORD m_tlsIndex;
~CThreadLocalObject();
};

#endif // __AFXTLS_H__

*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
///////////////////////////////////////////////////////
// AFXTLS.CPP文件



#include "_AFXTLS_.H"


//------------------CSimpleList类---------------------------
//添加节点
void CSimpleList::AddHead(void* p)
{
*GetNextPtr(p) = m_pHead;
m_pHead = p;
}
//删除结点
BOOL CSimpleList::Remove(void* p)
{
if(p == NULL) // 检查参数
return FALSE;

BOOL bResult = FALSE; // 假设移除失败
if(p == m_pHead)
{
// 要移除头元素
m_pHead = *GetNextPtr(p);
bResult = TRUE;
}
else
{
// 试图在表中查找要移除的元素
void* pTest = m_pHead;
while(pTest != NULL && *GetNextPtr(pTest) != p)
pTest = *GetNextPtr(pTest);

// 如果找到,就将元素移除
if(pTest != NULL)
{
*GetNextPtr(pTest) = *GetNextPtr(p);
bResult = TRUE;
}
}
return bResult;
}






//-------------------CThreadSlotData类----------------------//
BYTE __afxThreadData[sizeof(CThreadSlotData)]; // 为下面的_afxThreadData变量提供内存
CThreadSlotData* _afxThreadData; // 定义全局变量_afxThreadData来为全局变量分配空间

//槽状态结构
struct CSlotData
{
DWORD dwFlags; // 槽的使用标志(被分配/未被分配)
HINSTANCE hInst;// 占用此槽的模块句柄
};
//链表节点结构
struct CThreadData : public CNoTrackObject
{
CThreadData* pNext; // CSimpleList类要使用此成员
int nCount; // 数组元素的个数
LPVOID* pData; // 数组的首地址
};

#define SLOT_USED 0x01 // CSlotData结构中dwFlags成员的值为0x01时表示该槽已被使用
//构造函数,前期的初始化
CThreadSlotData::CThreadSlotData()
{
m_list.Construct(offsetof(CThreadData, pNext)); // 初始化CTypedSimpleList对象

m_nMax = 0;
m_nAlloc = 0;
m_nRover = 1; // 我们假定Slot1还未被分配(第一个槽(Slot0)总是保留下来不被使用)
m_pSlotData = NULL;

m_tlsIndex = ::TlsAlloc(); // 使用系统的TLS申请一个索引
::InitializeCriticalSection(&m_cs); // 初始化关键段变量
}

//在位数组上获取可用的槽号
int CThreadSlotData::AllocSlot()
{
::EnterCriticalSection(&m_cs); // 进入临界区(也叫关键段)
int nAlloc = m_nAlloc;
int nSlot = m_nRover;

//如果当前要使用的槽号大于位数组个数,或当前槽号已经被使用
if(nSlot >= nAlloc || m_pSlotData[nSlot].dwFlags & SLOT_USED)
{
// 搜索m_pSlotData,查找空槽(SLOT)
for(nSlot = 1; nSlot < nAlloc && m_pSlotData[nSlot].dwFlags & SLOT_USED; nSlot ++) ;

// 如果不存在空槽,申请更多的空间
if(nSlot >= nAlloc)
{
// 增加全局数组的大小,分配或再分配内存以创建新槽
int nNewAlloc = nAlloc + 32;

HGLOBAL hSlotData;
if(m_pSlotData == NULL) // 第一次使用
{
hSlotData = ::GlobalAlloc(GMEM_MOVEABLE, nNewAlloc*sizeof(CSlotData));
}
else
{
//获取当前位数组的句柄
hSlotData = ::GlobalHandle(m_pSlotData);
//位数组从物理内存中解除
::GlobalUnlock(hSlotData);
//重新设置位数组的大小
hSlotData = ::GlobalReAlloc(hSlotData,
nNewAlloc*sizeof(CSlotData), GMEM_MOVEABLE);
}
//将位数组绑定到物理内存中
CSlotData* pSlotData = (CSlotData*)::GlobalLock(hSlotData);

// 将新申请的空间初始化为0
memset(pSlotData + m_nAlloc, 0, (nNewAlloc - nAlloc)*sizeof(CSlotData));
m_nAlloc = nNewAlloc;
m_pSlotData = pSlotData;
}
}

// 调整m_nMax的值,以便为各线程的私有数据分配内存
if(nSlot >= m_nMax)
m_nMax = nSlot + 1;

m_pSlotData[nSlot].dwFlags |= SLOT_USED;
// 更新m_nRover的值(我们假设下一个槽未被使用)
m_nRover = nSlot + 1;

::LeaveCriticalSection(&m_cs);
return nSlot; // 返回的槽号可以被FreeSlot, GetThreadValue, SetValue函数使用了
}

void CThreadSlotData::FreeSlot(int nSlot)
{
::EnterCriticalSection(&m_cs);

// 删除所有线程中的数据
CThreadData* pData = m_list;
while(pData != NULL)
{
if(nSlot < pData->nCount)
{
delete (CNoTrackObject*)pData->pData[nSlot];
pData->pData[nSlot] = NULL;
}
pData = pData->pNext;
}

// 将此槽号标识为未被使用
m_pSlotData[nSlot].dwFlags &= ~SLOT_USED;
::LeaveCriticalSection(&m_cs);
}

inline void* CThreadSlotData::GetThreadValue(int nSlot)
{
CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex);
if(pData == NULL || nSlot >= pData->nCount)
return NULL;
return pData->pData[nSlot];
}

void CThreadSlotData::SetValue(int nSlot, void* pValue)
{
// 通过TLS索引得到我们为线程安排的私有存储空间
CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex);

// 为线程私有数据申请内存空间
if((pData == NULL || nSlot >= pData->nCount) && pValue != NULL)
{
// pData的值为空,表示该线程第一次访问线程私有数据
if(pData == NULL)
{
pData = new CThreadData;
pData->nCount = 0;
pData->pData = NULL;

// 将新申请的内存的地址添加到全局列表中
::EnterCriticalSection(&m_cs);
m_list.AddHead(pData);
::LeaveCriticalSection(&m_cs);
}

// pData->pData指向真正的线程私有数据,下面的代码将私有数据占用的空间增长到m_nMax指定的大小
if(pData->pData == NULL)
pData->pData = (void**)::GlobalAlloc(LMEM_FIXED, m_nMax*sizeof(LPVOID));
else
pData->pData = (void**)::GlobalReAlloc(pData->pData, m_nMax*sizeof(LPVOID), LMEM_MOVEABLE);

// 将新申请的内存初始话为0
memset(pData->pData + pData->nCount, 0,
(m_nMax - pData->nCount) * sizeof(LPVOID));
pData->nCount = m_nMax;
::TlsSetValue(m_tlsIndex, pData);
}

// 设置线程私有数据的值
pData->pData[nSlot] = pValue;
}

void CThreadSlotData::DeleteValues(HINSTANCE hInst, BOOL bAll)
{
::EnterCriticalSection(&m_cs);
if(!bAll)
{
// 仅仅删除当前线程的线程局部存储占用的空间
CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex);
if(pData != NULL)
DeleteValues(pData, hInst);
}
else
{
// 删除所有线程的线程局部存储占用的空间
CThreadData* pData = m_list.GetHead();
while(pData != NULL)
{
CThreadData* pNextData = pData->pNext;
DeleteValues(pData, hInst);
pData = pNextData;
}
}
::LeaveCriticalSection(&m_cs);
}

void CThreadSlotData::DeleteValues(CThreadData* pData, HINSTANCE hInst)
{
// 释放表中的每一个元素
BOOL bDelete = TRUE;
for(int i=1; i<pData->nCount; i++)
{
if(hInst == NULL || m_pSlotData[i].hInst == hInst)
{
// hInst匹配,删除数据
delete (CNoTrackObject*)pData->pData[i];
pData->pData[i] = NULL;
}
else
{
// 还有其它模块在使用,不要删除数据
if(pData->pData[i] != NULL)
bDelete = FALSE;
}
}

if(bDelete)
{
// 从列表中移除
::EnterCriticalSection(&m_cs);
m_list.Remove(pData);
::LeaveCriticalSection(&m_cs);
::LocalFree(pData->pData);
delete pData;

// 清除TLS索引,防止重用
::TlsSetValue(m_tlsIndex, NULL);
}
}

CThreadSlotData::~CThreadSlotData()
{
CThreadData *pData = m_list;
while(pData != NULL)
{
CThreadData* pDataNext = pData->pNext;
DeleteValues(pData, NULL);
pData = pData->pNext;
}

if(m_tlsIndex != (DWORD)-1)
::TlsFree(m_tlsIndex);

if(m_pSlotData != NULL)
{
HGLOBAL hSlotData = ::GlobalHandle(m_pSlotData);
::GlobalUnlock(hSlotData);
::GlobalFree(m_pSlotData);
}

::DeleteCriticalSection(&m_cs);
}

//---------------------------------------CNoTrackObject类---------------------------

void* CNoTrackObject::operator new(size_t nSize)
{
// 申请一块带有GMEM_FIXED和GMEM_ZEROINIT标志的内存
void* p = ::GlobalAlloc(GPTR, nSize);
return p;
}

void CNoTrackObject::operator delete(void* p)
{
if(p != NULL)
::GlobalFree(p);
}

//----------------------------CThreadLocalObject 类--------------------------------//

CNoTrackObject* CThreadLocalObject::GetData(CNoTrackObject* (*pfnCreateObject)())
{
if(m_nSlot == 0)
{
if(_afxThreadData == NULL)
_afxThreadData = new(__afxThreadData) CThreadSlotData;
m_nSlot = _afxThreadData->AllocSlot();
}

CNoTrackObject* pValue = (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
if(pValue == NULL)
{
// 创建一个数据项
pValue = (*pfnCreateObject)();

// 使用线程私有数据保存新创建的对象
_afxThreadData->SetValue(m_nSlot, pValue);
}

return pValue;
}

CNoTrackObject* CThreadLocalObject::GetDataNA()
{
if(m_nSlot == 0 || _afxThreadData == 0)
return NULL;
return (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
}

CThreadLocalObject::~CThreadLocalObject()
{
if(m_nSlot != 0 && _afxThreadData != NULL)
_afxThreadData->FreeSlot(m_nSlot);
m_nSlot = 0;
}

//------------------------------------------