关于windows程序设计的一些问题

来源:互联网 发布:单片机c51简易数字钟 编辑:程序博客网 时间:2024/05/20 08:26
本人自学Windows程序设计(第三版)这本书,用以学习windows api

,(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 0x01CSlotThreadData::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,但编译器报错。特写这篇日志标记这个错误,以往有人解决,或者以后在探寻。



原创粉丝点击