,(msdn看着有点费劲)。章节到第三章时,出现了一些问题,特在此整理一下。
在书中章节3.3设计自己的线程局部存储 中,作者所写的实例通过各线程共享一个CSlotThreadData对象,各线程分别新建自己的CNoTrackObject对象,使用一个m_nSlot变量通过调用CSlotThreadData对象的AllotSlot成员函数获得一个Slot槽。
但作者的代码有些问题,额,先贴源码吧。
_AFXTLS_.h文件
#ifndef _AFXTLS_H_
#define _AFXTLS_H_
#include<stdio.h>
#include<windows.h>
#include<stddef.h>
#include<iostream>
using namespace std;
using std::cout;
using std::endl;
class CSimpleList
{
public:
CSimpleList(int nNextOffSet = 0);
void Construct(int nNextOffset);
//给用户的函数接口
bool IsEmpty() const;
void AddHead(void* p);
void* GetHead() const;
bool Remove(void* p);
void RemoveAll();
void* GetNext(void* p) const;
//需要用到的成员
void* m_pHead;
int m_nNextOffSet;
void** GetNextPtr(void* p) const;
};
inline CSimpleList::CSimpleList(int nNextOffSet)
{
m_pHead = NULL;
m_nNextOffSet = nNextOffSet;
}
inline void CSimpleList::Construct(int nNextOffSet)
{
m_nNextOffSet = nNextOffSet;
}
inline bool CSimpleList::IsEmpty() const
{
return m_pHead == NULL;
}
inline void* CSimpleList::GetHead() const
{
return m_pHead;
}
inline void CSimpleList::RemoveAll()
{
m_pHead = NULL;
}
inline void** CSimpleList::GetNextPtr(void* p) const
{
return (void**)((BYTE*)p + m_nNextOffSet);
}
inline void* CSimpleList::GetNext(void* p) const
{
return *GetNextPtr(p);
}
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();
}
};
class CNoTrackObject
{
public:
CNoTrackObject(){ cout << "CNoTrackObject()" << endl; }
void* operator new(size_t nSize);
void operator delete(void* p);
virtual ~CNoTrackObject(){ cout << "~CNoTrackObject()" << endl; };
};
struct CSlotData;
struct CThreadData;
class CSlotThreadData
{
public:
//构造函数
CSlotThreadData();
//接口函数
int SlotAlloc();
void SlotFree(int nSlot);
void* GetThreadValue(int nSlot);
void SetThreadValue(int nSlot, void* pValue);
void DeleteValues(HINSTANCE hInst, bool bAll = false);
//成员
void DeleteValues(CThreadData* pData, HINSTANCE hInst);
void* operator new(size_t, void* p)
{
return p;
}
DWORD m_tlsIndex;
int m_nAlloc;
int m_nRover;
int m_nMax;
CSlotData* m_pSlotData;
CTypedSimpleList<CThreadData*> m_list;
CRITICAL_SECTION m_cs;
//析构函数
~CSlotThreadData();
};
class CThreadLocalObject
{
public:
CThreadLocalObject(){ cout << "CThreadLocalObject()" << endl; };
CNoTrackObject* GetData(CNoTrackObject* (*pCreateObject)());
CNoTrackObject* GetDataNA();
int m_nSlot;
~CThreadLocalObject();
};
template<class TYPE>
class CThreadLocal :public CThreadLocalObject
{
public:
static CNoTrackObject* CreateObject()
{
return new TYPE;
}
TYPE* GetData()
{
return (TYPE*)CThreadLocalObject::GetData(&CreateObject);
}
TYPE* GetDataNA()
{
return (TYPE*)CThreadLocalObject::GetDataNA();
}
operator TYPE*()
{
return GetData();
}
TYPE* operator ->()
{
return GetData();
}
};
#endif
_AFXTLS_.cpp文件
#include "stdafx.h"
#include"_AFXTLS_.h"
#ifdef _AFXTLS_H_
using namespace std;
using std::cout;
using std::endl;
void CSimpleList::AddHead(void* p)
{
*GetNextPtr(p) = m_pHead;
m_pHead = p;
}
bool CSimpleList::Remove(void* p)
{
//如果p为空 结束函数
if (p == NULL)
return false;
bool bRet = false;//先假定删除失败
if (p == m_pHead)
{
m_pHead = GetNext(p);
}
else
{
void* pTest = m_pHead;
while (pTest != NULL && p != GetNext(pTest))
{
pTest = GetNext(pTest);
}
if (pTest == NULL)
{
printf("pTest == NULL\n");
return bRet;
}
*GetNextPtr(pTest) = GetNext(p);
}
bRet = true;
return bRet;
}
void* CNoTrackObject::operator new(size_t nSize)
{
void* p = ::GlobalAlloc(GPTR, nSize);
return p;
}
void CNoTrackObject::operator delete(void* p)
{
if (p != NULL)
{
::GlobalFree(p);
}
}
struct CSlotData
{
DWORD dwFlags;
HINSTANCE hInst;
};
struct CThreadData :public CNoTrackObject
{
CThreadData* pNext;
int nCount;
LPVOID* pData;
};
#define SLOT_USED 0x01
CSlotThreadData::CSlotThreadData()
{
cout << "CSlotThreadData()" << endl;
m_tlsIndex = ::TlsAlloc();
m_list.Construct(offsetof(CThreadData, pNext));
m_nAlloc = 0;
m_nMax = 0;
m_nRover = 1;
m_pSlotData = NULL;
::InitializeCriticalSection(&m_cs);
}
int CSlotThreadData::SlotAlloc()
{
::EnterCriticalSection(&m_cs);
int nAlloc = m_nAlloc;
int nSlot = m_nRover;
if (nSlot >= nAlloc || m_pSlotData[nSlot].dwFlags & SLOT_USED)
{
for (nSlot = 1; nSlot<nAlloc && m_pSlotData[nSlot].dwFlags&SLOT_USED; nSlot++);
if (nSlot >= nAlloc)
{
int nNewAlloc = nAlloc + 32;
HGLOBAL p;
if (m_pSlotData == NULL)
{
p = ::GlobalAlloc(GMEM_MOVEABLE, sizeof(CSlotData)*nNewAlloc);
}
else
{
p = ::GlobalHandle(m_pSlotData);
::GlobalUnlock(p);
p = ::GlobalReAlloc(p, sizeof(CSlotData)*nNewAlloc, GMEM_MOVEABLE);
}
CSlotData* pData = (CSlotData*)::GlobalLock(p);
memset(pData + nAlloc, 0, (nNewAlloc - nAlloc)*sizeof(CSlotData));
m_pSlotData = pData;
m_nAlloc = nNewAlloc;
}
}
if (nSlot >= m_nMax)
{
m_nMax = nSlot + 1;
}
m_pSlotData[nSlot].dwFlags |= SLOT_USED;
m_nRover = nSlot + 1;
::LeaveCriticalSection(&m_cs);
return nSlot;
}
void CSlotThreadData::SetThreadValue(int nSlot, void* pValue)
{
CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex);
if ((pData == NULL || pData->nCount <= nSlot) && pValue != NULL)
{
if (pData == NULL)
{
pData = new CThreadData;
pData->nCount = 0;
pData->pData = NULL;
::EnterCriticalSection(&m_cs);
m_list.AddHead(pData);
::LeaveCriticalSection(&m_cs);
}
if (pData->pData == NULL)
{
pData->pData = (void**)::LocalAlloc(LMEM_FIXED, sizeof(LPVOID)*m_nMax);
}
else
{
pData->pData = (void**)::LocalReAlloc(pData->pData, sizeof(LPVOID)*m_nMax, LMEM_MOVEABLE);
}
memset(pData->pData + pData->nCount, 0, sizeof(LPVOID)*(m_nMax - pData->nCount));
pData->nCount = m_nMax;
::TlsSetValue(m_tlsIndex, pData);
}
pData->pData[nSlot] = pValue;
}
void* CSlotThreadData::GetThreadValue(int nSlot)
{
CThreadData* pData = (CThreadData*)::TlsGetValue(m_tlsIndex);
if (pData == NULL || pData->nCount <= nSlot)
{
return NULL;
}
return pData->pData[nSlot];
}
void CSlotThreadData::SlotFree(int nSlot)
{
::EnterCriticalSection(&m_cs);
CThreadData* pData = m_list;
while (pData != NULL)
{
if (pData->nCount > nSlot)
{
delete (CNoTrackObject*)pData->pData[nSlot];
pData->pData[nSlot] = NULL;
}
pData = pData->pNext;
}
m_pSlotData[nSlot].dwFlags &= ~SLOT_USED;
::LeaveCriticalSection(&m_cs);
}
void CSlotThreadData::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;
while (pData != NULL)
{
CThreadData* pNextData = pData->pNext;
DeleteValues(pData, hInst);
pData = pNextData;
}
}
::LeaveCriticalSection(&m_cs);
}
void CSlotThreadData::DeleteValues(CThreadData* pData, HINSTANCE hInst)
{
bool bDelete = true;
for (int i = 1; i<pData->nCount; i++)
{
if (m_pSlotData[i].hInst == hInst || hInst == NULL)
{
delete (CNoTrackObject*)pData->pData[i];
pData->pData[i] = NULL;
}
else
{
if (m_pSlotData[i].hInst != NULL)
{
bDelete = false;
}
}
}
if (bDelete)
{
::EnterCriticalSection(&m_cs);
m_list.Remove(pData);
::LeaveCriticalSection(&m_cs);
::LocalFree(pData->pData);
delete pData;
::TlsSetValue(m_tlsIndex, NULL);
}
}
CSlotThreadData::~CSlotThreadData()
{
CThreadData* pData = m_list;
while (pData != NULL)
{
CThreadData* p = pData->pNext;
DeleteValues(pData, NULL);
pData = p;
}
if (m_tlsIndex != (DWORD)-1)
{
::TlsFree(m_tlsIndex);
}
if (m_pSlotData != NULL)
{
HGLOBAL hSlotData = ::GlobalHandle(m_pSlotData);
::GlobalUnlock(hSlotData);
::GlobalFree(hSlotData);
}
::DeleteCriticalSection(&m_cs);
cout << "~CSlotThreadData()" << endl;
}
extern CRITICAL_SECTION m;
BYTE __afxThreadData[sizeof(CSlotThreadData)];
CSlotThreadData* _afxThreadData;
CNoTrackObject* CThreadLocalObject::GetData(CNoTrackObject* (*pCreateObject)())
{
::EnterCriticalSection(&m);
if (_afxThreadData == NULL || m_nSlot == 0)
{
if (_afxThreadData == NULL)
{
_afxThreadData = new (__afxThreadData)CSlotThreadData;
}
m_nSlot = _afxThreadData->SlotAlloc();
}
::LeaveCriticalSection(&m);
CNoTrackObject* pValue = (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
if (pValue == NULL)
{
pValue = (*pCreateObject)();//new CNoTrackObject
_afxThreadData->SetThreadValue(m_nSlot, pValue);
}
return pValue;
}
CNoTrackObject* CThreadLocalObject::GetDataNA()
{
if (_afxThreadData == NULL || m_nSlot == 0)
{
return NULL;
}
return (CNoTrackObject*)_afxThreadData->GetThreadValue(m_nSlot);
}
CThreadLocalObject::~CThreadLocalObject()
{
cout << "~CThreadLocalObject()" << endl;
if (m_nSlot != 0 && _afxThreadData != NULL)
_afxThreadData->SlotFree(m_nSlot);
m_nSlot = 0;
}
#endif
main.cpp文件
#include "stdafx.h"
#include "_AFXTLS_.h"
using namespace std;
using std::cout;
using std::endl;
#include<process.h>
CRITICAL_SECTION m;
extern CSlotThreadData* _afxThreadData;
struct CMyThreadData :public CNoTrackObject
{
int nSomeData;
};
//THREAD_LOCAL(CMyThreadData, g_myThreadData)
CThreadLocal<CMyThreadData> g_myThreadData;
void ShowData();
UINT WINAPI ThreadFunc(LPVOID lpParam)
{
CMyThreadData* t = g_myThreadData;
t->nSomeData = (int)lpParam;
//g_myThreadData->nSomeData = (int)lpParam;
ShowData();
return 0;
}
void test12()
{
HANDLE h[10];
UINT uID;
::InitializeCriticalSection(&m);
for (int i = 0; i < 10; i++)
h[i] = (HANDLE) ::_beginthreadex(NULL, 0, ThreadFunc, (void*)i, 0, &uID);
::WaitForMultipleObjects(10, h, TRUE, INFINITE);
for (int i = 0; i < 10; i++)
::CloseHandle(h[i]);
//delete _afxThreadData;
}
void test13()
{
HANDLE h;
//HANDLE h2;
UINT uID;
int i = 1;
int j = 2;
//_afxThreadData = new(__afxThreadData)CSlotThreadData;
::InitializeCriticalSection(&m);
h = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, (void*)i, 0, &uID);
//h2 = (HANDLE)::_beginthreadex(NULL, 0, ThreadFunc, (void*)j, 0, &uID);
::WaitForSingleObject(h, INFINITE);
//::WaitForSingleObject(h2, INFINITE);
::CloseHandle(h);
/**if (_afxThreadData != NULL)
{
delete[] _afxThreadData;
_afxThreadData = NULL;
}**/
::DeleteCriticalSection(&m);
//::CloseHandle(h2);
}
void ShowData()
{
int nData = g_myThreadData->nSomeData;
printf("ThreadID::%-5d,nSomeData=%d\n", ::GetCurrentThreadId(), nData);
}
int _tmain(int argc, _TCHAR* argv[])
{
test13();
return 0;
}
if (_afxThreadData == NULL || m_nSlot == 0)
{
if (_afxThreadData == NULL)
{
_afxThreadData = new (__afxThreadData)CSlotThreadData;
}
m_nSlot = _afxThreadData->SlotAlloc();
}
这是CThreadLocalObject::GetData方法中的部分代码;
原作者在检验_afxThreadData和初始化的时候,并没有考虑到多线程的影响,这导致实际上_afxThreadData可能会多次初始化,
通过调试,或者为CSlotThreadData构造函数添加输出,我们都能观察到,程序构造不止了一个CSlotThreadData对象。未解决这个问题,我在上面这段代码的基础上做了改进,
首先添加一个全局CRITICAL_SECTION m;变量在主线程中初始化::InitializeCriticalSection(&m);,并在上面这段代码的前后添加::EnterCriticalSection(&m);和::LeaveCriticalSection(&m);
::EnterCriticalSection(&m);
if (_afxThreadData == NULL || m_nSlot == 0)
{
if (_afxThreadData == NULL)
{
_afxThreadData = new (__afxThreadData)CSlotThreadData;
}
m_nSlot = _afxThreadData->SlotAlloc();
}
::LeaveCriticalSection(&m);
这样,各个线程实现了共享同一个_afxThreadData。而不会重复构造CSlotThreadData。但还有问题尚未解决,实际上这个_afxThreadData直到程序结束也未调用过析构函数,这就造成了内存泄漏,我本想在主线程中人工的添加delete ——afThreadData,但编译器报错。特写这篇日志标记这个错误,以往有人解决,或者以后在探寻。