参考资料 王艳平 《Windows程序设计》
#pragma once
#include <windows.h>
#include <stddef.h>
#include<iostream>
using namespace std;
class CNoTrackObject;
class CSimpleList//将每个线程的私有数据的首地址串联起来
{
public:
CSimpleList(int NextOffset = 0);
void Construct(int NextOffset);
// 提供给用户的接口函数(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_NextOffset; // 数据结构中pNext成员的偏移量
void** GetNextPtr(void* p) const;
};
// 类的内联函数
inline CSimpleList::CSimpleList(int nNextOffset)
{ m_pHead = NULL; m_NextOffset = nNextOffset; }
inline void CSimpleList::Construct(int NextOffset)
{ m_NextOffset = NextOffset; }
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); }
inline void** CSimpleList::GetNextPtr(void* p) const
{ return (void**)((BYTE*)p + m_NextOffset); }
//实现自动类型转换
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(); }
};
//如何为线程私有数据分配内存
//重写operator new delete
//让所有线程的私有哦数据使用的结构的都从此类继承
////////////////////////////////////////////////
// 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;//成员指针指向真正的线程私有数据,把pData指向的空间分成多个槽//这些槽(slot)组成PVOID类型的数组,每一个元素保存一个指针,即线程私有数据指针,
//它指向堆中分配的内存
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; // 迄今为止占用槽的最大数目
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();
};
struct CSlotData
{
DWORD dwFlags; // 槽的使用标志(被分配/未被分配)
HINSTANCE hInst;// 占用此槽的模块句柄
};
struct CThreadData : public CNoTrackObject
{
CThreadData* pNext; // CSimpleList类要使用此成员
int nCount; // 数组元素的个数
LPVOID* pData; // 数组的首地址
};
///////////////////////////////////////////////
///////////////////////////////////////////////
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(class_name, ident_name) CThreadLocal<class_name> ident_name;
#define EXTERN_THREAD_LOCAL(class_name, ident_name) extern THREAD_LOCAL(class_name, ident_name)
// MYTLS.cpp : 定义控制台应用程序的入口点。
//
#include "MYTLS.h"
BYTE __MYThreadData[sizeof(CThreadSlotData)]; // 为下面的MYThreadData变量提供内存
CThreadSlotData* _MYThreadData; // 定义全局变量MYThreadData来为全局变量分配空间
#define SLOT_USED 0x01 // CSlotData结构中dwFlags成员的值为0x01时表示该槽已被使用
void CSimpleList::AddHead(void* p)
{
*GetNextPtr(p) = m_pHead;//p的pNext指向原表头
m_pHead = p;
}
BOOL CSimpleList::Remove(void* p)
{
if(p == NULL)
return FALSE;
BOOL IsResult = FALSE; // 假设移除失败
if(p == m_pHead)
{
// 要移除头元素
m_pHead = *GetNextPtr(p);
IsResult = TRUE;
}
else
{
// 试图在表中查找要移除的元素
void* pTest = m_pHead;
while(pTest != NULL && *GetNextPtr(pTest) != p)
pTest = *GetNextPtr(pTest);
// 如果找到,就将元素移除
if(pTest != NULL)
{
*GetNextPtr(pTest) = *GetNextPtr(p);
IsResult = TRUE;
}
}
return IsResult;
}
//---------------------------------------
void* CNoTrackObject::operator new(size_t nSize)
{
//GHND 申请一块带有GMEM_MOVEABLE和GMEM_ZEROINIT标志的内存
//GMEM_MOVEABLE 可移动 返回内存对象的句柄 用GlobalLock将句柄转换为指针
//GMEM_FIXED 固定 返回内存指针
//GMEMZEROINIT 初始化为0
// GPTR申请一块带有GMEM_FIXED和GMEM_ZEROINIT标志的内存
void* p = ::GlobalAlloc(GPTR, nSize);
return p;
}
void CNoTrackObject::operator delete(void* p)
{
if(p != NULL)
::GlobalFree(p);
}
//-------------------CThreadSlotData类----------------------//
CThreadSlotData::CThreadSlotData()
{
//进来后先进构造函数 初始化部分变量
//
m_list.Construct(offsetof(CThreadData, pNext)); // 初始化CTypedSimpleList对象 计算得到偏移量赋给m_NextOffset
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;//开始0
int nSlot = m_nRover;// 1 第一个槽保留
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);//Decrements the lock count associated with a memory object that was allocated with GMEM_MOVEABLE. This function has no effect on memory objects allocated with GMEM_FIXED.
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];//用的是CNoTrackObject重载的
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* v1 = (CThreadData*)TlsGetValue(m_TLSIndex);//利用系统的TLS
// 为线程私有数据申请内存空间
if((v1 == NULL || nSlot >= v1->nCount) && pValue != NULL)
{
// pData的值为空,表示该线程第一次访问线程私有数据
if(v1 == NULL)//初始化v1
{
v1 = new CThreadData;
v1->nCount = 0;
v1->pData = NULL;
// 将新申请的内存的地址添加到全局列表中
EnterCriticalSection(&m_cs);
m_list.AddHead(v1);//设置各个数据头部CtreadData结构的pNext值
LeaveCriticalSection(&m_cs);
}
// v1->pData指向真正的线程私有数据,下面的代码将私有数据占用的空间增长到m_nMax指定的大小
if(v1->pData == NULL)
v1->pData = (void**)::GlobalAlloc(LMEM_FIXED, m_nMax*sizeof(LPVOID));
else
v1->pData = (void**)::GlobalReAlloc(v1->pData, m_nMax*sizeof(LPVOID), LMEM_MOVEABLE);
// 将新申请的内存初始话为0
memset(v1->pData + (v1->nCount)* sizeof(LPVOID), 0,
(m_nMax - v1->nCount) * sizeof(LPVOID));//源代码此处为v1->pData + v1->nCount ,但我认为是v1->pData + (v1->nCount)* sizeof(LPVOID)
v1->nCount = m_nMax;//类似偏移
TlsSetValue(m_TLSIndex, v1);
}
// 设置线程私有数据的值
v1->pData[nSlot] = pValue;//pValue是个地址
}
void CThreadSlotData::DeleteValues(HINSTANCE hInst, BOOL IsAll)
{
EnterCriticalSection(&m_cs);
if(!IsAll)
{
// 仅仅删除当前线程的线程局部存储占用的空间
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* v1, HINSTANCE hInst)
{
// 释放表中的每一个元素
BOOL bDelete = TRUE;
for(int i=1; i<v1->nCount; i++)
{
if(hInst == NULL || m_pSlotData[i].hInst == hInst)
{
// hInst匹配,删除数据
delete (CNoTrackObject*)v1->pData[i];
v1->pData[i] = NULL;
}
else
{
// 还有其它模块在使用,不要删除数据
if(v1->pData[i] != NULL)
bDelete = FALSE;//只要有其他槽还在使用就将bDelete置为FALSE
}
}
if(bDelete)//当还有有其他槽还在使用就将bDelete置为FALSE,不进
{
// 从列表中移除
EnterCriticalSection(&m_cs);
m_list.Remove(v1);
LeaveCriticalSection(&m_cs);
LocalFree(v1->pData);
delete v1;
// 清除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);
}
//----------------------------CThreadLocalObject 类--------------------------------//
CNoTrackObject* CThreadLocalObject::GetData(CNoTrackObject* (*pfnCreateObject)())
{
if(m_nSlot == 0)//变量没分配槽号
{
if(_MYThreadData == NULL)
_MYThreadData = new(__MYThreadData)CThreadSlotData;//此处new重构 只返回参数中的指针作为对象的首地址 BYTE __MYThreadData[sizeof(CThreadSlotData)];
m_nSlot = _MYThreadData->AllocSlot();//变为1
}
CNoTrackObject* pValue = (CNoTrackObject*)_MYThreadData->GetThreadValue(m_nSlot);//当槽里没有东西时返回null
if(pValue == NULL)
{
// 创建一个数据项
pValue = (*pfnCreateObject)();
// 使用线程私有数据保存新创建的对象
_MYThreadData->SetValue(m_nSlot, pValue);
}
return pValue;
}
CNoTrackObject* CThreadLocalObject::GetDataNA()
{
if(m_nSlot == 0 || _MYThreadData == 0)
return NULL;
return (CNoTrackObject*)_MYThreadData->GetThreadValue(m_nSlot);
}
CThreadLocalObject::~CThreadLocalObject()
{
if(m_nSlot != 0 && _MYThreadData != NULL)
_MYThreadData->FreeSlot(m_nSlot);
m_nSlot = 0;
}
//------------------------------------------
#include"MYTLS.h"
#include <process.h>
#include <iostream>
using namespace std;
struct CMyThreadData : public CNoTrackObject
{
int nSomeData;
};
CThreadLocal<CMyThreadData> g_myThreadData;//运用模版,减少转换
void ShowData();
UINT __stdcall ThreadFunc(LPVOID lpParam)
{
g_myThreadData->nSomeData = (int)lpParam;//此处->重载
ShowData();
return 0;
}
void main()
{
HANDLE h[10];
UINT uID;
// 启动十个线程,将i做为线程函数的参数传过去
for(int i=0; i<1; i++)
h[i] = (HANDLE) _beginthreadex(NULL, 0, ThreadFunc, (void*)(i+1), 0, &uID);//i作为参数传进去
WaitForMultipleObjects(1, h, TRUE, INFINITE);
for(int i=0; i<1; i++)
CloseHandle(h[i]);
getchar();
}
void ShowData()
{
int nData = g_myThreadData->nSomeData;
printf(" Thread ID: %-5d, nSomeData = %d \n", GetCurrentThreadId(), nData);
}
所有评论(0)