您的位置:首页 > 编程语言

[置顶] 关于线程局部存储代码出错分析讨论

2016-06-30 17:24 393 查看
忙了一两天把这个代码敲完了,但是在调试时候出 了一些问题,慕思苦想还没搞定这个问题多以请大家指点指点



_AFXTLS.h文件如下:

/******************************************************************* 

 *  Copyright(c) 2000-2016 Company Name 

 *  All rights reserved. 

 *   

 *  文件名称: _AFXTLS.h

 *  简要描述: 

 *   

 *  创建日期: 2016.6.30

 *  作者: 

 *  说明: 

 ******************************************************************/  

#ifndef __AFXTLS_H__

#define __AFXTLS_H__

#include "stdafx.h"

#include "windows.h"

#include <rpcndr.h>

#define offsetof(s,m) (size_t)&(((s*)0)->m)

#define SLOT_USED 0x01

// --------CSimpList - provide a simple function------------------

class CSimpList

{

public:
~CSimpList();
void **GetNextPtr(void *p) const
{
return  (void **)((BYTE*)p+m_nNextOffset);
}
CSimpList(int nNextOffSet = 0);
void Construct(int nNextOffSet);
BOOL IsEmpty()const;
void AddHead(void *p);
void RemoveAll();
void* GetHead()const;
void *GetNext(void *p)const;
BOOL Remove(void *p);

private:
void *m_pHead;//指向链表第一个位置
size_t m_nNextOffset;//数据结构中pNext成员的偏移量

};

inline CSimpList::CSimpList(int nNextOffSet)

{
m_pHead = NULL;
this->m_nNextOffset = nNextOffSet;

}

inline CSimpList::~CSimpList()

{

}

inline void CSimpList::Construct(int nNextOffSet)

{
this->m_nNextOffset = nNextOffSet;

}

inline BOOL CSimpList::IsEmpty() const

{
return m_pHead ==NULL;

}

inline void CSimpList::RemoveAll()

{
m_pHead =NULL;

}

inline void* CSimpList::GetHead() const

{
return m_pHead;

}

inline void * CSimpList::GetNext(void *p) const

{
return GetNextPtr(p);

}

inline BOOL CSimpList::Remove(void *p)

{
if (m_pHead == 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(p);
}
if(pTest !=NULL)
{
*GetNextPtr(pTest) = *GetNextPtr(p);
bResult = true;
}
}
return bResult;

}

inline void CSimpList::AddHead(void *p)

{
*GetNextPtr(p)=m_pHead;
m_pHead = p;

}

// --------CTypedSimpleList - extend CSimpleList------------------

template<class TYPE>

class CTypedSimpleList:public CSimpList

{

public:
CTypedSimpleList(int nNextOffSet = 0): CSimpList(nNextOffSet){}
void AddHead(TYPE p)
{
CSimpList::AddHead((void *)p);
}
TYPE GetHead()
{
return (TYPE)CSimpList::GetHead();
}
TYPE GetNext(TYPE p)
{
return (TYPE)CSimpList::GetNext(p);
}
BOOL Remove(TYPE p)
{
return CSimpList::Remove(p);
}

/*
CTypeSimpleList <CThreadData *>list
CThreadData *pData = list;等价于pData = list.GetHead
*/
operator TYPE()//直接引用类的对象会调用此函数
{
return  (TYPE)CSimpList::GetHead();
}

};

// --------CNoTrackObject --------------------------------------------

class CNoTrackObject //负责私有线程低层内存分配

{

public:
void *operator new(size_t nSize);
void operator delete(void *p);
virtual ~CNoTrackObject();

};

struct CThreadData :public CNoTrackObject  
//线程的私有数据

{
CThreadData *pNext;//指向下一个结构指针
LPVOID
*Data; //真正指向的数据
int nCount;
// current size of pData

};

// --------CSlotData --------------------------------------------

struct CSlotData

{
DWORD dwflag;
//槽的使用标志,使用或未使用
HINSTANCE hInst;

};

// --------CThreadSlotData --------------------------------------------

class CThreadSlotData

{

public:
CThreadSlotData();
~CThreadSlotData();
int AllocSlot();//分配槽
void FreeSlot(int nSlot);//释放槽
void *GetThreadValue(int nSlot);
void SetValue(int nSlot,void *pValue);
void DeleteValue(HINSTANCE hinst,BOOL bAll =FALSE);
DWORD m_tlsIndex;
CTypedSimpleList <CThreadData *> m_list;
int m_nAlloc;
//m_pSlotData所指向的数组大小
int m_nMax;
//占用槽的最大数
CSlotData * m_pSlotdata;//全局数组的首地址
int m_nRover;
//为了快速找到空闲的槽而设定的值
CRITICAL_SECTION m_cs;
void *operator new (size_t,void *p)
{
return p;
}
void DeleteValues(CThreadData *pData,HINSTANCE hinst);

};

class CThreadLocalObject

{

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

//具体实现
DWORD m_nSlot;

};

template <class TYPE>

class CThreadLocal:public CThreadLocalObject

{
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();
}

static CNoTrackObject* CreateObject()
{
return new TYPE;
}

};

#endif

_AFXTLS.cpp文件如下:

#include "stdafx.h"

#include "_AFXTLS.h"

#include "windows.h"

#include <winbase.h>

void *CNoTrackObject::operator new(size_t nSize)

{
void *p = ::GlobalAlloc(GPTR,nSize);
return p;

}

void CNoTrackObject::operator delete(void *p)

{
::GlobalFree(p);

}

CNoTrackObject::~CNoTrackObject()

{

}

CThreadSlotData::CThreadSlotData()

{
m_list.Construct(offsetof(CThreadData,pNext));
m_nMax = 0;
m_nAlloc = 0;
m_nRover = 1;
//假设SLOT1还未分配,Slot0总是被保留
m_pSlotdata = NULL;//指向槽的指针
m_tlsIndex = ::TlsAlloc();
::InitializeCriticalSection(&m_cs);

}

int CThreadSlotData::AllocSlot()

{
::EnterCriticalSection(&m_cs);

int nAlloc = m_nAlloc;
int nSlot = m_nRover;
if (nSlot>=nAlloc || m_pSlotdata[nSlot].dwflag & SLOT_USED)
{
for (nSlot = 1;nSlot<nAlloc && m_pSlotdata[nSlot].dwflag & SLOT_USED;nSlot++)
{
//如果不存在空槽申请更多的空间
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);
m_nAlloc  = nNewAlloc;
m_pSlotdata = pSlotData;

}
}
if(nSlot >= m_nMax)
{
m_nMax = nSlot+1;
}
m_pSlotdata[nSlot].dwflag |=SLOT_USED;//标志为该标记已经使用
m_nRover = nSlot+1;//更新m_nRover假设下一个槽未被使用
::LeaveCriticalSection(&m_cs);

return nSlot;

}

BYTE __afxThreadData[sizeof CThreadData]; //为下面的__afxThreadData变量提供内存

CThreadSlotData *_afxThreadData; //定义全局变量__afxThreadData来为全局变量分配内存

void CThreadSlotData::SetValue(int nSlot,void *pValue)

{
CThreadData *pData = (CThreadData* )::TlsGetValue(m_tlsIndex);//通过tls索引为线程分配私有空间
//为线程私有数据分配空间
if((pData ==NULL || nSlot>=pData->nCount)&& pValue !=NULL)
{
if (pData ==NULL)
{
pData = new CThreadData;
pData->nCount = 0;
pData->Data = NULL;

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

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

else
{
pData->Data = (void **)::GlobalReAlloc(pData->Data,m_nMax*sizeof(LPVOID),LMEM_MOVEABLE);
}

//将申请的内存初始化为0;
memset(&(pData->Data) + pData->nCount,0,(m_nMax-pData->nCount)*sizeof(LPVOID));
pData->nCount=m_nMax;
::TlsSetValue(m_tlsIndex,pData);
}
pData->Data[nSlot] = pValue;

}

void *CThreadSlotData::GetThreadValue(int nSlot)

{
CThreadData *pData=(CThreadData *)TlsGetValue(m_tlsIndex);
if (pData==NULL || nSlot>=pData->nCount)
{
return NULL;
}
return pData->Data[nSlot];

}

void CThreadSlotData:: FreeSlot(int nSlot)

{
::EnterCriticalSection(&m_cs);
CThreadData *pData = m_list;
while (pData !=NULL)
{
if (nSlot<pData->nCount)
{
delete((CNoTrackObject*)pData->Data[nSlot]);
pData->Data[nSlot] = NULL;
pData = pData->pNext;
}

}
//将此槽标示为未被使用
m_pSlotdata[nSlot].dwflag &=~SLOT_USED;
::LeaveCriticalSection(&m_cs);

}

void CThreadSlotData::DeleteValue(HINSTANCE hinst,BOOL bAll /*=FALSE*/)

{
::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->Data[i]);
pData->Data[i] = NULL;

else
{
//还有其他模块在使用不要删除数据
if(pData->Data[i]!=NULL)
bDelete =FALSE;
}
if (bDelete)
{
//从列表中删除
::EnterCriticalSection(&m_cs);
m_list.Remove(pData);
::LeaveCriticalSection(&m_cs);
::LocalFree(pData->Data);
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(hSlotData);
}
::DeleteCriticalSection(&m_cs);

}

CThreadLocalObject::CThreadLocalObject()

{

}

CThreadLocalObject::~CThreadLocalObject()

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

}

CNoTrackObject * CThreadLocalObject::GetData(CNoTrackObject* (*pfnCreateObject)())

{
if (m_nSlot = NULL)
{
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);

}

主文件TLS.cpp文件如下:

// TLS.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include "windows.h"

#include "_AFXTLS.h"

#include "process.h"

#pragma comment(lib,"ws2_32.lib")

void ShowData();

struct CMyThreadData:public CNoTrackObject

{
int nSomeData;

};

CThreadLocal<CMyThreadData>g_myThreadData;

UINT __stdcall ThreadFunc(LPVOID lpParam)

{
g_myThreadData->nSomeData =(int)lpParam;
ShowData();
return 0;

}

int _tmain(int argc, _TCHAR* argv[])\

{

HANDLE h[10];
UINT uID;

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]);
}

}

void ShowData()

{
int nData = g_myThreadData->nSomeData;
printf("Thread ID:%-5d,nSomeData = %d\n",::GetCurrentThreadId(),nData);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: