先贴代码:
_AFXTLS_.h:
1 #ifndef _AFXTLS_H_ 2 #define _AFXTLS_H_ 3 #include<stdio.h> 4 #include<windows.h> 5 #include<stddef.h> 6 #include<iostream> 7 using namespace std; 8 9 class CSimpleList 10 { 11 public: 12 CSimpleList(int nNextOffSet = 0); 13 void Construct(int nNextOffset); 14 //给用户的函数接口 15 bool IsEmpty() const; 16 void AddHead(void* p); 17 void* GetHead() const; 18 bool Remove(void* p); 19 void RemoveAll(); 20 void* GetNext(void* p) const; 21 //需要用到的成员 22 void* m_pHead; 23 int m_nNextOffSet; 24 void** GetNextPtr(void* p) const; 25 }; 26 inline CSimpleList::CSimpleList(int nNextOffSet) 27 { 28 m_pHead = NULL; 29 m_nNextOffSet = nNextOffSet; 30 } 31 inline void CSimpleList::Construct(int nNextOffSet) 32 { 33 m_nNextOffSet = nNextOffSet; 34 } 35 inline bool CSimpleList::IsEmpty() const 36 { 37 return m_pHead == NULL; 38 } 39 inline void* CSimpleList::GetHead() const 40 { 41 return m_pHead; 42 } 43 inline void CSimpleList::RemoveAll() 44 { 45 m_pHead = NULL; 46 } 47 inline void** CSimpleList::GetNextPtr(void* p) const 48 { 49 return (void**)((BYTE*)p + m_nNextOffSet); 50 } 51 inline void* CSimpleList::GetNext(void* p) const 52 { 53 return *GetNextPtr(p); 54 } 55 template<class TYPE> 56 class CTypedSimpleList : public CSimpleList 57 { 58 public: 59 CTypedSimpleList(int nNextOffSet = 0):CSimpleList(nNextOffSet){}; 60 void AddHead(TYPE p) 61 { 62 CSimpleList::AddHead((void*)p); 63 } 64 TYPE GetHead() const 65 { 66 return (TYPE)CSimpleList::GetHead(); 67 } 68 bool Remove(TYPE p) 69 { 70 return CSimpleList::Remove((void*)p); 71 } 72 TYPE GetNext(void* p) const 73 { 74 return (TYPE)CSimpleList::GetNext(p); 75 } 76 operator TYPE() 77 { 78 return (TYPE)CSimpleList::GetHead(); 79 } 80 }; 81 class CNoTrackObject 82 { 83 public: 84 void* operator new(size_t nSize); 85 void operator delete(void* p); 86 virtual ~CNoTrackObject(){}; 87 }; 88 struct CSlotData; 89 struct CThreadData; 90 class CSlotThreadData 91 { 92 public: 93 //构造函数 94 CSlotThreadData(); 95 //接口函数 96 int SlotAlloc(); 97 void SlotFree(int nSlot); 98 void* GetThreadValue(int nSlot); 99 void SetThreadValue(int nSlot,void* pValue); 100 void DeleteValues(HINSTANCE hInst,bool bAll = false); 101 //成员 102 void DeleteValues(CThreadData* pData,HINSTANCE hInst); 103 void* operator new(size_t,void* p) 104 { 105 return p; 106 } 107 DWORD m_tlsIndex; 108 int m_nAlloc; 109 int m_nRover; 110 int m_nMax; 111 CSlotData* m_pSlotData; 112 CTypedSimpleList<CThreadData*> m_list; 113 CRITICAL_SECTION m_cs; 114 //析构函数 115 ~CSlotThreadData(); 116 }; 117 118 class CThreadLocalObject 119 { 120 public: 121 CNoTrackObject* GetData(CNoTrackObject* (*pCreateObject)()); 122 CNoTrackObject* GetDataNA(); 123 int m_nSlot; 124 ~CThreadLocalObject(); 125 }; 126 template<class TYPE> 127 class CThreadLocal:public CThreadLocalObject 128 { 129 public: 130 static CNoTrackObject* CreateObject() 131 { 132 return new TYPE; 133 } 134 TYPE* GetData() 135 { 136 return (TYPE*)CThreadLocalObject::GetData(&CreateObject); 137 } 138 TYPE* GetDataNA() 139 { 140 return (TYPE*)CThreadLocalObject::GetDataNA(); 141 } 142 operator TYPE*() 143 { 144 return GetData(); 145 } 146 TYPE* operator ->() 147 { 148 return GetData(); 149 } 150 }; 151 152 #endif
_AFXTLS_.cpp:
1 #include"_AFXTLS_.h" 2 #ifdef _AFXTLS_H_ 3 void CSimpleList::AddHead(void* p) 4 { 5 *GetNextPtr(p) = m_pHead; 6 m_pHead = p; 7 } 8 9 bool CSimpleList::Remove(void* p) 10 { 11 //如果p为空 结束函数 12 if(p == NULL) 13 return false; 14 15 bool bRet = false;//先假定删除失败 16 17 if(p == m_pHead) 18 { 19 m_pHead = GetNext(p); 20 } 21 else 22 { 23 void* pTest = m_pHead; 24 while(pTest != NULL && p != GetNext(pTest)) 25 { 26 pTest = GetNext(pTest); 27 } 28 if(pTest == NULL) 29 { 30 printf("pTest == NULL "); 31 return bRet; 32 } 33 *GetNextPtr(pTest) = GetNext(p); 34 } 35 bRet = true; 36 return bRet; 37 } 38 39 void* CNoTrackObject::operator new(size_t nSize) 40 { 41 void* p = ::GlobalAlloc(GPTR,nSize); 42 return p; 43 } 44 void CNoTrackObject::operator delete(void* p) 45 { 46 if(p != NULL) 47 { 48 ::GlobalFree(p); 49 } 50 } 51 52 BYTE __afxThreadData[sizeof(CSlotThreadData)]; 53 CSlotThreadData* _afxThreadData; 54 struct CSlotData 55 { 56 DWORD dwFlags; 57 HINSTANCE hInst; 58 }; 59 struct CThreadData:public CNoTrackObject 60 { 61 CThreadData* pNext; 62 int nCount; 63 LPVOID* pData; 64 }; 65 66 #define SLOT_USED 0x01 67 68 CSlotThreadData::CSlotThreadData() 69 { 70 m_tlsIndex = ::TlsAlloc(); 71 m_list.Construct(offsetof(CThreadData,pNext)); 72 m_nAlloc = 0; 73 m_nMax = 0; 74 m_nRover = 1; 75 m_pSlotData = NULL; 76 ::InitializeCriticalSection(&m_cs); 77 } 78 int CSlotThreadData::SlotAlloc() 79 { 80 ::EnterCriticalSection(&m_cs); 81 82 int nAlloc = m_nAlloc; 83 int nSlot = m_nRover; 84 if(nSlot >= nAlloc||m_pSlotData[nSlot].dwFlags & SLOT_USED) 85 { 86 for(nSlot = 1;nSlot<nAlloc && m_pSlotData[nSlot].dwFlags&SLOT_USED;nSlot++); 87 if(nSlot >= nAlloc) 88 { 89 int nNewAlloc = nAlloc + 32; 90 HGLOBAL p; 91 if(m_pSlotData == NULL) 92 { 93 p = ::GlobalAlloc(GMEM_MOVEABLE,sizeof(CSlotData)*nNewAlloc); 94 } 95 else 96 { 97 p = ::GlobalHandle(m_pSlotData); 98 ::GlobalUnlock(p); 99 p = ::GlobalReAlloc(p,sizeof(CSlotData)*nNewAlloc,GMEM_MOVEABLE); 100 } 101 CSlotData* pData = (CSlotData*)::GlobalLock(p); 102 memset(pData+nAlloc,0,(nNewAlloc-nAlloc)*sizeof(CSlotData)); 103 m_pSlotData = pData; 104 m_nAlloc = nNewAlloc; 105 } 106 } 107 if(nSlot >= m_nMax) 108 { 109 m_nMax = nSlot+1; 110 } 111 m_pSlotData[nSlot].dwFlags |= SLOT_USED; 112 m_nRover = nSlot+1; 113 ::LeaveCriticalSection(&m_cs); 114 return nSlot; 115 } 116 117 void CSlotThreadData::SetThreadValue(int nSlot,void* pValue) 118 { 119 CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex); 120 if((pData==NULL||pData->nCount <= nSlot)&&pValue != NULL) 121 { 122 if(pData == NULL) 123 { 124 pData = new CThreadData; 125 pData->nCount = 0; 126 pData->pData = NULL; 127 ::EnterCriticalSection(&m_cs); 128 m_list.AddHead(pData); 129 ::LeaveCriticalSection(&m_cs); 130 } 131 if(pData->pData == NULL) 132 { 133 pData->pData = (void**)::LocalAlloc(LMEM_FIXED,sizeof(LPVOID)*m_nMax); 134 } 135 else 136 { 137 pData->pData = (void**)::LocalReAlloc(pData->pData,sizeof(LPVOID)*m_nMax,LMEM_MOVEABLE); 138 } 139 memset(pData->pData+pData->nCount,0,sizeof(LPVOID)*(m_nMax - pData->nCount)); 140 pData->nCount = m_nMax; 141 ::TlsSetValue(m_tlsIndex,pData); 142 } 143 pData->pData[nSlot] = pValue; 144 } 145 146 void* CSlotThreadData::GetThreadValue(int nSlot) 147 { 148 CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex); 149 if(pData == NULL||pData->nCount<= nSlot) 150 { 151 return NULL; 152 } 153 return pData->pData[nSlot]; 154 } 155 156 void CSlotThreadData::SlotFree(int nSlot) 157 { 158 ::EnterCriticalSection(&m_cs); 159 CThreadData* pData = m_list; 160 while(pData != NULL) 161 { 162 if(pData->nCount > nSlot) 163 { 164 delete (CNoTrackObject*) pData->pData[nSlot]; 165 pData->pData[nSlot] = NULL; 166 } 167 pData = pData->pNext; 168 } 169 m_pSlotData[nSlot].dwFlags &= ~SLOT_USED; 170 ::LeaveCriticalSection(&m_cs); 171 } 172 173 void CSlotThreadData::DeleteValues(HINSTANCE hInst,bool bAll ) 174 { 175 ::EnterCriticalSection(&m_cs); 176 if(!bAll) 177 { 178 CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex); 179 if(pData != NULL) 180 { 181 DeleteValues(pData,hInst); 182 } 183 } 184 else 185 { 186 CThreadData* pData = m_list; 187 while(pData !=NULL) 188 { 189 CThreadData* pNextData = pData->pNext; 190 DeleteValues(pData,hInst); 191 pData = pNextData; 192 } 193 } 194 ::LeaveCriticalSection(&m_cs); 195 } 196 197 void CSlotThreadData::DeleteValues(CThreadData* pData,HINSTANCE hInst) 198 { 199 bool bDelete = true; 200 for(int i = 1;i<pData->nCount;i++) 201 { 202 if(m_pSlotData[i].hInst == hInst||hInst == NULL) 203 { 204 delete (CNoTrackObject*) pData->pData[i]; 205 pData->pData[i] = NULL; 206 } 207 else 208 { 209 if(m_pSlotData[i].hInst != NULL) 210 { 211 bDelete = false; 212 } 213 } 214 } 215 if(bDelete) 216 { 217 ::EnterCriticalSection(&m_cs); 218 m_list.Remove(pData); 219 ::LeaveCriticalSection(&m_cs); 220 ::LocalFree(pData->pData); 221 delete pData; 222 ::TlsSetValue(m_tlsIndex,NULL); 223 } 224 } 225 226 CSlotThreadData::~CSlotThreadData() 227 { 228 CThreadData* pData = m_list; 229 while(pData != NULL) 230 { 231 CThreadData* p = pData->pNext; 232 DeleteValues(pData,NULL); 233 pData = p; 234 } 235 if(m_tlsIndex != (DWORD)-1) 236 { 237 ::TlsFree(m_tlsIndex); 238 } 239 if(m_pSlotData!= NULL) 240 { 241 HGLOBAL hSlotData = ::GlobalHandle(m_pSlotData); 242 ::GlobalUnlock(hSlotData); 243 ::GlobalFree(hSlotData); 244 } 245 ::DeleteCriticalSection(&m_cs); 246 } 247 248 CNoTrackObject* CThreadLocalObject::GetData(CNoTrackObject* (*pCreateObject)()) 249 { 250 if( _afxThreadData == NULL || m_nSlot == 0) 251 { 252 if(_afxThreadData == NULL) 253 { 254 _afxThreadData = new (__afxThreadData)CSlotThreadData; 255 } 256 m_nSlot = _afxThreadData->SlotAlloc(); 257 } 258 CNoTrackObject* pValue = (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot); 259 if(pValue == NULL) 260 { 261 pValue = (*pCreateObject)(); 262 _afxThreadData->SetThreadValue(m_nSlot,pValue); 263 } 264 return pValue; 265 } 266 267 CNoTrackObject* CThreadLocalObject::GetDataNA() 268 { 269 if(_afxThreadData == NULL || m_nSlot == 0) 270 { 271 return NULL; 272 } 273 return (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot); 274 } 275 CThreadLocalObject::~CThreadLocalObject() 276 { 277 if(m_nSlot!=0&&_afxThreadData != NULL) 278 _afxThreadData->SlotFree(m_nSlot); 279 m_nSlot = 0; 280 } 281 #endif
_AFXSTAT.h:
1 #ifndef _AFXSTATE_H_ 2 3 #define _AFXSTATE_H_ 4 5 #ifndef _AFXTLS_H_ 6 #include"_AFXTLS_.h" 7 #endif 8 9 class CWinThread; 10 11 class AFX_MODULE_THREAD_STATE:public CNoTrackObject 12 { 13 public: 14 CWinThread* m_pCurrentWinThread; 15 }; 16 AFX_MODULE_THREAD_STATE* AfxGetModuleThreadState(); 17 extern CThreadLocal<AFX_MODULE_THREAD_STATE> _afxModuleThreadState; 18 #endif
_AFXSTATE.cpp:
1 #include"_AFXSTAT.h" 2 #ifdef _AFXSTATE_H_ 3 AFX_MODULE_THREAD_STATE* AfxGetModuleThreadState() 4 { 5 return _afxModuleThreadState.GetData(); 6 } 7 CThreadLocal<AFX_MODULE_THREAD_STATE> _afxModuleThreadState; 8 #endif
_AFXWIN.h:
1 #ifndef _AFXWIN_H_ 2 #define _AFXWIN_H_ 3 4 #include"_AFXSTAT.h" 5 //#include<process.h> 6 typedef UINT (__cdecl *AFX_THREADPROC)(LPVOID); 7 8 class CWinThread 9 { 10 public: 11 CWinThread(); 12 bool CreateThread(UINT dwStackSize = 0,DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL); 13 14 bool m_bAutoDelete; 15 HANDLE m_hThread; 16 UINT m_uId; 17 18 operator HANDLE () 19 { 20 return this==NULL?NULL:m_hThread; 21 } 22 23 int GetThreadPriority() 24 { 25 return ::GetThreadPriority(m_hThread); 26 } 27 bool SetThreadPriority(int nPriority) 28 { 29 return ::SetThreadPriority(m_hThread,nPriority); 30 } 31 32 DWORD SuspendThread() 33 { 34 return ::SuspendThread(m_hThread); 35 } 36 DWORD ResumeThread() 37 { 38 return ::ResumeThread(m_hThread); 39 } 40 41 virtual void Delete(); 42 virtual ~CWinThread(); 43 void CommonConstruct(); 44 45 AFX_THREADPROC m_pfnThreadProc; 46 LPVOID m_pThreadParams; 47 48 CWinThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam); 49 50 }; 51 CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam, 52 int nPriority = THREAD_PRIORITY_NORMAL,UINT nStackSize = 0, 53 DWORD dwCreateFlags = 0,LPSECURITY_ATTRIBUTES lpSecurityAtts = NULL); 54 CWinThread* AfxGetThread(); 55 void AfxEndThread(UINT nExitCode,bool bDelete = true); 56 #endif
THRDCORE.cpp:
1 #include"_AFXWIN.h" 2 #include"_AFXTLS_.h" 3 extern CSlotThreadData* _afxThreadData; 4 struct _AFX_THREAD_STARTUP 5 { 6 CWinThread* pThread; 7 HANDLE hEvent1; 8 HANDLE hEvent2; 9 bool bError; 10 }; 11 UINT __stdcall _AfxThreadEntry(void* pParam) 12 { 13 _AFX_THREAD_STARTUP* pStartup = (_AFX_THREAD_STARTUP*)pParam; 14 CWinThread* pThread = pStartup->pThread; 15 try 16 { 17 AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); 18 pState->m_pCurrentWinThread = pThread; 19 } 20 catch(...) 21 { 22 pStartup->bError = true; 23 ::SetEvent(pStartup->hEvent1); 24 AfxEndThread((UINT)-1,false); 25 } 26 HANDLE hEvent2 = pStartup->hEvent2; 27 ::SetEvent(pStartup->hEvent1); 28 ::WaitForSingleObject(hEvent2,INFINITE); 29 ::CloseHandle(hEvent2); 30 UINT nResult = (*pThread->m_pfnThreadProc)(pThread->m_pThreadParams); 31 AfxEndThread(nResult); 32 return 0; 33 } 34 bool CWinThread::CreateThread(UINT dwStackSize ,DWORD dwCreateFlags ,LPSECURITY_ATTRIBUTES lpThreadAttributes ) 35 { 36 _AFX_THREAD_STARTUP startup ; 37 startup.bError = false; 38 startup.pThread = this; 39 startup.hEvent1 = ::CreateEvent(NULL,true,false,NULL); 40 startup.hEvent2 = ::CreateEvent(NULL,true,false,NULL); 41 m_hThread = (HANDLE)::_beginthreadex(NULL,0,_AfxThreadEntry,&startup,dwCreateFlags|CREATE_SUSPENDED,&m_uId); 42 if(m_hThread == NULL) 43 { 44 return false; 45 } 46 ::ResumeThread(m_hThread); 47 ::WaitForSingleObject(startup.hEvent1,INFINITE); 48 ::CloseHandle(startup.hEvent1); 49 if(dwCreateFlags&CREATE_SUSPENDED) 50 { 51 ::SuspendThread(m_hThread); 52 } 53 if(startup.bError) 54 { 55 ::WaitForSingleObject(m_hThread,INFINITE); 56 ::CloseHandle(m_hThread); 57 m_hThread = NULL; 58 ::CloseHandle(startup.hEvent2); 59 return false; 60 } 61 ::SetEvent(startup.hEvent2); 62 return true; 63 } 64 CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam, 65 int nPriority ,UINT nStackSize , 66 DWORD dwCreateFlags,LPSECURITY_ATTRIBUTES lpSecurityAtts) 67 { 68 CWinThread* pThread = new CWinThread(pfnThreadProc,pParam); 69 if(!pThread->CreateThread(nStackSize,dwCreateFlags,lpSecurityAtts)) 70 { 71 pThread->Delete(); 72 return NULL; 73 } 74 pThread->SetThreadPriority(nPriority); 75 if(!(dwCreateFlags&CREATE_SUSPENDED)) 76 { 77 pThread->ResumeThread(); 78 } 79 return pThread; 80 } 81 void AfxEndThread(UINT nExitCode,bool bDelete ) 82 { 83 AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); 84 CWinThread* pThread = pState->m_pCurrentWinThread; 85 if(pThread!=NULL) 86 { 87 if(bDelete) 88 { 89 pThread->Delete(); 90 } 91 pState->m_pCurrentWinThread = NULL; 92 } 93 if(_afxThreadData != NULL) 94 { 95 _afxThreadData->DeleteValues(NULL,false); 96 } 97 _endthreadex(nExitCode); 98 } 99 CWinThread* AfxGetThread() 100 { 101 AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); 102 return pState->m_pCurrentWinThread; 103 } 104 void CWinThread::CommonConstruct() 105 { 106 m_hThread = NULL; 107 m_uId = 0; 108 m_bAutoDelete = true; 109 } 110 CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam) 111 { 112 m_pfnThreadProc = pfnThreadProc; 113 m_pThreadParams = pParam; 114 CommonConstruct(); 115 } 116 CWinThread::CWinThread() 117 { 118 m_pfnThreadProc = NULL; 119 m_pThreadParams = NULL; 120 CommonConstruct(); 121 } 122 CWinThread::~CWinThread() 123 { 124 if(m_hThread != NULL) 125 { 126 ::CloseHandle(m_hThread); 127 } 128 AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); 129 if(pState->m_pCurrentWinThread == this) 130 { 131 pState->m_pCurrentWinThread = NULL; 132 } 133 } 134 void CWinThread::Delete() 135 { 136 if(m_bAutoDelete) 137 { 138 delete this; 139 } 140 }
Test.cpp:
1 #include<iostream> 2 #include"_AFXWIN.h" 3 using namespace std; 4 UINT MyFunc(LPVOID lpParam) 5 { 6 printf("Thread Identify:%d ",AfxGetThread()->m_uId); 7 return 0; 8 } 9 int main() 10 { 11 for(int i = 0; i<10;i++) 12 { 13 AfxBeginThread(MyFunc,NULL); 14 } 15 system("pause"); 16 }
--------------------------------------------分割线-----------------------------------------------------
哎哟( ̄y▽, ̄)╭ ,写了这么长时间的这玩意,也总算是写完了,那么我们从头来讲。
起初,他是需要设计一个可以无限申请TLS索引而又可以动态管理指针单元所指向内存单元的分配和释放的系统。在书中作者的意思是:利用现有的TLS系统,在这个系统基础上再建立一个新的符合上述条件的TLS系统。这样想来,我们可以画出我们这个TLS系统的内存模型图。如下所示:
这样想来其实是很简单的,我们只需要为每个线程的私有存储空间即线程的私有空指针分配一段空指针数组的内存即可,那么我们用的时候,就可以在每个线程中使用不同槽位置的空指针并为其分配内存即可。可是这样就有问题了,第一我们不知道我们为每个线程所分配的这个空指针数组的大小,如果我们约定一个固定的数,比如我们为每个线程都分配32个空指针大小的数组,那么当我们所需要的槽即我们所需要的这些个空指针数目超过32个该怎么办呢?那么如果我们申请一块内存保存这个数,让每个线程的数组大小都保持这么多可以吗?肯定也是不行的!因为假如我们在线程1中用了很多这样的私有内存,而在线程2中没有用到或者用的很少的时候,我们还为线程2分配那么多的内存,岂不是产生了很多的内存浪费?所以这样也不行!那么记录一个数不行,如果我们为每个线程申请一个私有的内存保存这个线程私有数组的大小行不行呢?显然是可以的,这样既保证了对数组大小的无限分配,又保证了内存不会出现大量浪费。那么我们把这个东西保存在哪里呢?显然我们可以把线程私有数组的头指针和这个数字变量保存在一起,合成一个结构体,我们把这个结构体叫做CThreadData。如下:
struct CThreadData
{
int nCount;
LPVOID pData;
};
这样来看的话,我们的内存模型就变成了:
这样解决了上一个问题。可是,我们TLS是需要为每个线程分配空槽,那么我们的哪个槽正在使用中?哪个槽被释放?被占用的槽的占用资源是什么?我们都不知道,可能我们会把已经占用的槽都给分配出去,这显然是不允许的。所以我们还需要为每个槽保存一个结构体,形成一个结构体数组,结构体中包含了上面两者的状态,叫做CSlotData。如下:
struct CSlotData
{
HANDLE hInst;
DWORD dwFlags;
};
这样的话,我们就必须保存这个数组的头指针,由于我们需要的是不定长的结构体数组大小,所以我们还需要一个整型变量来保存这个数组的大小。两个变量结合成一个类CThreadSlotData。如下:
class CThreadSlotData
{
public:
CSlotData* m_pSlotData;
int m_nAlloc;
};
如果我们每次申请新槽都需要重新分配内存,那么显然是不现实的。所以我们最好是一次性分配一定的量,在用完之后,再分配一定的量,如此循环。我们可以再保存一个变量存储已使用过的最大号槽的号数+1,变量叫做m_nRover,这保证了m_nRover号及之后的槽总是可以使用的。这样可以了吗?这时候我们再来想一个问题,假如我们每次为CSlotData数组增加32个位置,那是不是也意味着我们也需要为每个线程的私有数组在新分配32个位置,但如果我们有的线程根本就用不到这么长的数组空间,那岂不是浪费了很多,所以我们再保存一个变量用来存储m_nRover+1,变量名为m_nMax,这样可以保证浪费变得很少。
理论上来讲,这里的m_nMax是和m_nRover相同或大于即可的,但是作者这么写了,我们就也先写这个数。
所以,类代码如下:
class CThreadSlotData
{
public:
CSlotData* m_pSlotData;
int m_nAlloc;
int m_nRover;
int m_nMax;
};
这样来讲的话,我们可以再次画出它的内存模型,如下:
注:CThreadSlotData类图中,为节省图面空间,没有写其他几个成员。
想到这里,就有了如上的内存模型图。但是我们的类是为了其功能而创建的,所以我们需要清晰一下它的功能:
1.要能够申请槽位和释放槽位。
2.要能够存取我们任意类型的数据。
3.能够在线程结束时释放所有申请的内存。
4.能够自动为我们的数据申请内存并保存。
我们把上述功能在聚集到上面所述的类CThreadSlotData中,再加上类本身的构造和析构函数,另外类在申请系统的TLS时,需要保存申请的Index号数,得到如下:
CThreadSlotData
{
public:
CThreadSlotData();
int AllocSlot();
void FreeSlot(int nSlot);
void* GetThreadValue(int nSlot);
void SetValue(int nSlot,void* pValue);
DWORD m_tlsIndex;
int m_nAlloc;
int m_nRover;
int m_nMax;
CSlotData* m_pSlotData;
~CThreadSlotData();
};
现在我们来想一下,我们如何释放掉所有线程的同一个槽位?
再回看一下上面的内存模型,我们可以做到吗?显然,我们不可能在线程内部释放。所以我们必须有办法在主线程内获取所有其它线程申请的私有内存。这也就是说,我们必须在类中保存一个进入其他线程私有存储的方法,当然,我们也许还可以用一个数组把所有的私有内存地址保存下来,但更好的是,我们将不同线程私有内存的指针用链表连接起来,这样就可以很简单的在主线程里释放槽位了。
当然,现在的问题是,我们如何在这个类里加上链表?我们可以选择直接在类内写一个链表,可是这样这个类太过臃肿,而且代码耦合性会很高,出问题的话很难解决,链表本身和这个类关系要求又不是特别高,有一定通用性,所以更好的办法是:我们封装一个链表类,然后在这个类中声明出来。这样的代码独立性更高,复用性更高,更加所谓“面向对象”。
那么接下来,就是这个链表类CSimpleList:
class CSimpleList
{
public:
CSimpleList(int nNextOffset=0);
void Construct(int nNextOffset);
bool IsEmpty() const;
void AddHead(void* p);
void RemoveAll();
void* GetHead() const;
bool Remove(void* p);
void* m_pHead;
size_t m_nNextOffset;
void** GetNextPtr(void* p)const;
};
具体实现也就不再讲了,具体这里很多函数为什么声明其实作者也讲地比较清楚了,如果各位还有什么不懂的话,可以联系问我。
接下来是它的模板类CSimpleListTyped。如下:
template<class TYPE>
class CTypedSimpleList : public CSimpleList
{
public:
CTypedSimpleList(int nNextOffSet = 0):CSimpleList(nNextOffSet){};
void AddHead(TYPE p)
{
CSimpleList::AddHead((void*)p);
}
TYPE GetHead() const
{
return (TYPE)CSimpleList::GetHead();
}
bool Remove(TYPE p)
{
return CSimpleList::Remove((void*)p);
}
TYPE GetNext(void* p) const
{
return (TYPE)CSimpleList::GetNext(p);
}
operator TYPE()
{
return (TYPE)CSimpleList::GetHead();
}
};
这个类出现是为了更好的封装和使用上述链表,这个类中我觉得也并不难,唯一可能大家会不明白的一点就是最后一个函数operator TYPE()了。这个函数是个类类型转换,有关这个大家可以直接百度类类型转换,就会一目了然。如果还有其他的什么,也可以直接问,我就不再赘述了。
有了这个类,我们就可以把线程私有的连接起来,但链表单元也需要有指向下一单元的指针,所以我们需要在线程私有的结构体CThreadData中保存一个这样的指针。于是结构体变成了:
struct CThreadData
{
CThreadData* pNext;
int nCount;
LPVOID pData;
};
接着只需要在类中声明这样一个成员就可以了。
但这样足够了吗?显然还不够,你需要能够释放任一线程任意模块的能力,所以有了如下成员函数:
void DeleteValues(HINSTANCE hInst,bool bAll = false);//删除任意模块,可选是否删除所有线程的
void DeleteValues(CThreadData* pData,HINSTANCE hInst);//删除本线程的任一模块
//以上所有的hInst == NULL的情况都是删除所有的意思。
两个函数嵌套调用即可实现这种能力。
另外,这些线程在执行时,必然会有同时访问同一段内存的情况发生,比如同时添加链表单元。所以,这个类成员还需要保证多线程执行的互锁函数和其相关成员。这样的话,我们再添加上成员:
CRITICAL_SECTION m_cs;
这时候这个类的内存模型就变成了:
接近完美!但还差一点儿。理论上,这个类是可以申请TLS_MINIMUN_AVAILABLE个对象的,但是其实对于这个类来说,一个也就够了,因为其本身可以申请足够多个我们所设计的系统所提供的槽位,所以这是足够的。所以我们直接为其声明为一个全局指针,利用重载new方法,将一段已经声明的全局内存地址传过去,这时候就可以利用new操作符的特性,让这个对象自动调用构造函数构造其本身。至于为什么这么麻烦而不是直接声明一个全局的类对象,原因很简单,因为书上是这么写的,而我是不知道为什么的~~~,,ԾㅂԾ,,~~~。我做了好多实验,个人认为从这个角度上讲是可以直接声明一个类对象的,但作者没这么做,而是绕了一个大圈,如此繁琐实现了这个样子,我想以后的扩展代码也许自有其用途吧。
这样的话,我们把这个运算符重载加上去,类的设计则接近完成。
但我们可以直接把这个类给用户使用吗?肯定是不行的。这个类还缺少了为系统槽内指针分配内存的能力,这是其一;其二这个系统本身也是不够坚固,很多函数并不足够健壮,例如系统本身在你不申请到特定槽号的情况下也是可以直接存取那个槽号的槽指针的值的;其三,很扯淡的一个原因,不够面向对象,这样子存取本身和面向过程方法不无区别;其四,若是用户创建很多个这样的类,当数目超过TLS_MINIMUN_AVAILABLE时,就会将系统的所有Tls申请完毕,之后的类对象就不能够在进行使用,造成很多其他困扰。
足够符合上述条件的,最好的是这个类每个对象对应一个槽,这样使用方便,且符合条件三和四,在使用时规范使用这个类,就可以忽略上述健壮性问题,那么最后我们可以用模板类来实现上述的分配内存的问题。这样的话,类CThreadLocalObject和类CThreadLocal完美符合了这个条件。可是假如我们定义了一个CThreadLocal<MyStructData> g_myStructData;那么我们如何访问MyStructData内部的成员呢?这就是CThreadLocal类中 TYPE* operator->(); 运算符重载函数的作用,有关这个运算符重载大家可以看我的其他文章,上面有其他讲的很好的链接,此处不再赘述。
那么前面的内存部分就到此结束了。可是我们这样还不够自动化,我们需要最好是那种线程自动清理的TLS,所以需要把原有的线程开始结束函数等封装一下,让他可以在线程结束时,自动清理所有的东西。
我们需要一个类,用它来管理线程的挂起唤醒设置权限线程创建等功能。这个类要能够保存他所管理的线程的线程句柄以及线程ID。这样想来有了类CWinThread的原型:
class CWinThread
{
public:
CWinThread();
bool CreateThread(DWORD dwCreateFlags = 0,UINT nStackSize = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
HANDLE m_hThread;
operator HANDLE() const {return m_hThread;}
DWORD m_nThreadID;
int GetThreadPriority();
bool SetThreadPriority(int nPriority);
DWORD SuspendThread();
DWORD ResumeThread();
};
但如作者所说,一个线程的维护是还需要其他很多东西的,所以,作者再封装了一个可扩展的类AFX_MODULE_THREAD_STATE。此处不再多写。
那么我们在创建一个线程的时候,怎样才能够检测到线程结束以便于做清理工作呢?当然,我们可以利用事件机制进行通信,这就是后面有关其他函数封装的原理了。后面的很多东西其实作者就讲的比较清楚了,我这里就不再多说了(其实是累死我了不想再写了)。
当然,如果读者有什么问题可以在直接联系我,可以大家一起研究解决问题。
--------------------------------------------分割线-----------------------------------------------------
哎哟( ̄y▽, ̄)╭ ,写了这么长时间的这玩意,也总算是写完了,那么我们从头来讲。
起初,他是需要设计一个可以无限申请TLS索引而又可以动态管理指针单元所指向内存单元的分配和释放的系统。在书中作者的意思是:利用现有的TLS系统,在这个系统基础上再建立一个新的符合上述条件的TLS系统。这样想来,我们可以画出我们这个TLS系统的内存模型图。如下所示:
这样想来其实是很简单的,我们只需要为每个线程的私有存储空间即线程的私有空指针分配一段空指针数组的内存即可,那么我们用的时候,就可以在每个线程中使用不同槽位置的空指针并为其分配内存即可。可是这样就有问题了,第一我们不知道我们为每个线程所分配的这个空指针数组的大小,如果我们约定一个固定的数,比如我们为每个线程都分配32个空指针大小的数组,那么当我们所需要的槽即我们所需要的这些个空指针数目超过32个该怎么办呢?那么如果我们申请一块内存保存这个数,让每个线程的数组大小都保持这么多可以吗?肯定也是不行的!因为假如我们在线程1中用了很多这样的私有内存,而在线程2中没有用到或者用的很少的时候,我们还为线程2分配那么多的内存,岂不是产生了很多的内存浪费?所以这样也不行!那么记录一个数不行,如果我们为每个线程申请一个私有的内存保存这个线程私有数组的大小行不行呢?显然是可以的,这样既保证了对数组大小的无限分配,又保证了内存不会出现大量浪费。那么我们把这个东西保存在哪里呢?显然我们可以把线程私有数组的头指针和这个数字变量保存在一起,合成一个结构体,我们把这个结构体叫做CThreadData。如下:
struct CThreadData
{
int nCount;
LPVOID pData;
};
这样来看的话,我们的内存模型就变成了:
这样解决了上一个问题。可是,我们TLS是需要为每个线程分配空槽,那么我们的哪个槽正在使用中?哪个槽被释放?被占用的槽的占用资源是什么?我们都不知道,可能我们会把已经占用的槽都给分配出去,这显然是不允许的。所以我们还需要为每个槽保存一个结构体,形成一个结构体数组,结构体中包含了上面两者的状态,叫做CSlotData。如下:
struct CSlotData
{
HANDLE hInst;
DWORD dwFlags;
};
这样的话,我们就必须保存这个数组的头指针,由于我们需要的是不定长的结构体数组大小,所以我们还需要一个整型变量来保存这个数组的大小。两个变量结合成一个类CThreadSlotData。如下:
class CThreadSlotData
{
public:
CSlotData* m_pSlotData;
int m_nAlloc;
};
如果我们每次申请新槽都需要重新分配内存,那么显然是不现实的。所以我们最好是一次性分配一定的量,在用完之后,再分配一定的量,如此循环。我们可以再保存一个变量存储已使用过的最大号槽的号数+1,变量叫做m_nRover,这保证了m_nRover号及之后的槽总是可以使用的。这样可以了吗?这时候我们再来想一个问题,假如我们每次为CSlotData数组增加32个位置,那是不是也意味着我们也需要为每个线程的私有数组在新分配32个位置,但如果我们有的线程根本就用不到这么长的数组空间,那岂不是浪费了很多,所以我们再保存一个变量用来存储m_nRover+1,变量名为m_nMax,这样可以保证浪费变得很少。
理论上来讲,这里的m_nMax是和m_nRover相同或大于即可的,但是作者这么写了,我们就也先写这个数。
所以,类代码如下:
class CThreadSlotData
{
public:
CSlotData* m_pSlotData;
int m_nAlloc;
int m_nRover;
int m_nMax;
};
这样来讲的话,我们可以再次画出它的内存模型,如下:
注:CThreadSlotData类图中,为节省图面空间,没有写其他几个成员。
想到这里,就有了如上的内存模型图。但是我们的类是为了其功能而创建的,所以我们需要清晰一下它的功能:
1.要能够申请槽位和释放槽位。
2.要能够存取我们任意类型的数据。
3.能够在线程结束时释放所有申请的内存。
4.能够自动为我们的数据申请内存并保存。
我们把上述功能在聚集到上面所述的类CThreadSlotData中,再加上类本身的构造和析构函数,另外类在申请系统的TLS时,需要保存申请的Index号数,得到如下:
CThreadSlotData
{
public:
CThreadSlotData();
int AllocSlot();
void FreeSlot(int nSlot);
void* GetThreadValue(int nSlot);
void SetValue(int nSlot,void* pValue);
DWORD m_tlsIndex;
int m_nAlloc;
int m_nRover;
int m_nMax;
CSlotData* m_pSlotData;
~CThreadSlotData();
};
现在我们来想一下,我们如何释放掉所有线程的同一个槽位?
再回看一下上面的内存模型,我们可以做到吗?显然,我们不可能在线程内部释放。所以我们必须有办法在主线程内获取所有其它线程申请的私有内存。这也就是说,我们必须在类中保存一个进入其他线程私有存储的方法,当然,我们也许还可以用一个数组把所有的私有内存地址保存下来,但更好的是,我们将不同线程私有内存的指针用链表连接起来,这样就可以很简单的在主线程里释放槽位了。
当然,现在的问题是,我们如何在这个类里加上链表?我们可以选择直接在类内写一个链表,可是这样这个类太过臃肿,而且代码耦合性会很高,出问题的话很难解决,链表本身和这个类关系要求又不是特别高,有一定通用性,所以更好的办法是:我们封装一个链表类,然后在这个类中声明出来。这样的代码独立性更高,复用性更高,更加所谓“面向对象”。
那么接下来,就是这个链表类CSimpleList:
class CSimpleList
{
public:
CSimpleList(int nNextOffset=0);
void Construct(int nNextOffset);
bool IsEmpty() const;
void AddHead(void* p);
void RemoveAll();
void* GetHead() const;
bool Remove(void* p);
void* m_pHead;
size_t m_nNextOffset;
void** GetNextPtr(void* p)const;
};
具体实现也就不再讲了,具体这里很多函数为什么声明其实作者也讲地比较清楚了,如果各位还有什么不懂的话,可以联系问我。
接下来是它的模板类CSimpleListTyped。如下:
template<class TYPE>
class CTypedSimpleList : public CSimpleList
{
public:
CTypedSimpleList(int nNextOffSet = 0):CSimpleList(nNextOffSet){};
void AddHead(TYPE p)
{
CSimpleList::AddHead((void*)p);
}
TYPE GetHead() const
{
return (TYPE)CSimpleList::GetHead();
}
bool Remove(TYPE p)
{
return CSimpleList::Remove((void*)p);
}
TYPE GetNext(void* p) const
{
return (TYPE)CSimpleList::GetNext(p);
}
operator TYPE()
{
return (TYPE)CSimpleList::GetHead();
}
};
这个类出现是为了更好的封装和使用上述链表,这个类中我觉得也并不难,唯一可能大家会不明白的一点就是最后一个函数operator TYPE()了。这个函数是个类类型转换,有关这个大家可以直接百度类类型转换,就会一目了然。如果还有其他的什么,也可以直接问,我就不再赘述了。
有了这个类,我们就可以把线程私有的连接起来,但链表单元也需要有指向下一单元的指针,所以我们需要在线程私有的结构体CThreadData中保存一个这样的指针。于是结构体变成了:
struct CThreadData
{
CThreadData* pNext;
int nCount;
LPVOID pData;
};
接着只需要在类中声明这样一个成员就可以了。
但这样足够了吗?显然还不够,你需要能够释放任一线程任意模块的能力,所以有了如下成员函数:
void DeleteValues(HINSTANCE hInst,bool bAll = false);//删除任意模块,可选是否删除所有线程的
void DeleteValues(CThreadData* pData,HINSTANCE hInst);//删除本线程的任一模块
//以上所有的hInst == NULL的情况都是删除所有的意思。
两个函数嵌套调用即可实现这种能力。
另外,这些线程在执行时,必然会有同时访问同一段内存的情况发生,比如同时添加链表单元。所以,这个类成员还需要保证多线程执行的互锁函数和其相关成员。这样的话,我们再添加上成员:
CRITICAL_SECTION m_cs;
这时候这个类的内存模型就变成了:
接近完美!但还差一点儿。理论上,这个类是可以申请TLS_MINIMUN_AVAILABLE个对象的,但是其实对于这个类来说,一个也就够了,因为其本身可以申请足够多个我们所设计的系统所提供的槽位,所以这是足够的。所以我们直接为其声明为一个全局指针,利用重载new方法,将一段已经声明的全局内存地址传过去,这时候就可以利用new操作符的特性,让这个对象自动调用构造函数构造其本身。至于为什么这么麻烦而不是直接声明一个全局的类对象,原因很简单,因为书上是这么写的,而我是不知道为什么的~~~,,ԾㅂԾ,,~~~。我做了好多实验,个人认为从这个角度上讲是可以直接声明一个类对象的,但作者没这么做,而是绕了一个大圈,如此繁琐实现了这个样子,我想以后的扩展代码也许自有其用途吧。
这样的话,我们把这个运算符重载加上去,类的设计则接近完成。
但我们可以直接把这个类给用户使用吗?肯定是不行的。这个类还缺少了为系统槽内指针分配内存的能力,这是其一;其二这个系统本身也是不够坚固,很多函数并不足够健壮,例如系统本身在你不申请到特定槽号的情况下也是可以直接存取那个槽号的槽指针的值的;其三,很扯淡的一个原因,不够面向对象,这样子存取本身和面向过程方法不无区别;其四,若是用户创建很多个这样的类,当数目超过TLS_MINIMUN_AVAILABLE时,就会将系统的所有Tls申请完毕,之后的类对象就不能够在进行使用,造成很多其他困扰。
足够符合上述条件的,最好的是这个类每个对象对应一个槽,这样使用方便,且符合条件三和四,在使用时规范使用这个类,就可以忽略上述健壮性问题,那么最后我们可以用模板类来实现上述的分配内存的问题。这样的话,类CThreadLocalObject和类CThreadLocal完美符合了这个条件。可是假如我们定义了一个CThreadLocal<MyStructData> g_myStructData;那么我们如何访问MyStructData内部的成员呢?这就是CThreadLocal类中 TYPE* operator->(); 运算符重载函数的作用,有关这个运算符重载大家可以看我的其他文章,上面有其他讲的很好的链接,此处不再赘述。
那么前面的内存部分就到此结束了。可是我们这样还不够自动化,我们需要最好是那种线程自动清理的TLS,所以需要把原有的线程开始结束函数等封装一下,让他可以在线程结束时,自动清理所有的东西。
我们需要一个类,用它来管理线程的挂起唤醒设置权限线程创建等功能。这个类要能够保存他所管理的线程的线程句柄以及线程ID。这样想来有了类CWinThread的原型:
class CWinThread
{
public:
CWinThread();
bool CreateThread(DWORD dwCreateFlags = 0,UINT nStackSize = 0,LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
HANDLE m_hThread;
operator HANDLE() const {return m_hThread;}
DWORD m_nThreadID;
int GetThreadPriority();
bool SetThreadPriority(int nPriority);
DWORD SuspendThread();
DWORD ResumeThread();
};
但如作者所说,一个线程的维护是还需要其他很多东西的,所以,作者再封装了一个可扩展的类AFX_MODULE_THREAD_STATE。此处不再多写。
那么我们在创建一个线程的时候,怎样才能够检测到线程结束以便于做清理工作呢?当然,我们可以利用事件机制进行通信,这就是后面有关其他函数封装的原理了。后面的很多东西其实作者就讲的比较清楚了,我这里就不再多说了(其实是累死我了不想再写了)。
当然,如果读者有什么问题可以在直接联系我,可以大家一起研究解决问题。