• 设计自己的线程局部存储


    参考资料 王艳平 《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);
    }
    

      

  • 相关阅读:
    Android应用开发基础篇(16)-----ScaleGestureDetector(缩放手势检测)
    Android应用开发基础篇(15)-----URL(获取指定网址里的图片)
    Android应用开发基础篇(14)-----自定义标题栏
    Android应用开发提高篇(6)-----FaceDetector(人脸检测)
    Android应用开发提高篇(5)-----Camera使用
    Android应用开发提高篇(4)-----Socket编程(多线程、双向通信)
    Android应用开发基础篇(13)-----GestureDetector(手势识别)
    Android应用开发基础篇(12)-----Socket通信
    Android应用开发实例篇(1)-----简易涂鸦板
    Android应用开发提高篇(3)-----传感器(Sensor)编程
  • 原文地址:https://www.cnblogs.com/Crisczy/p/7398252.html
Copyright © 2020-2023  润新知