MFC实现线程局部变量时,引入了类CThreadSlotData,定义_afxThreadData  是class CThreadSlotData的指针,它的成员m_pSlotData是一个动态的数组,标示着全局的槽的分配情况。这个动态数组由AllocSlot负责查询、修改、更新。本人认真跟踪了AllocSlot()函数的算法,发现当前面的某个槽在使用过程中释放,再继续分配槽时,有可能把一个正在使用的槽进行再分配。AllocSlot()代码如下:
int CThreadSlotData::AllocSlot()
{//此函数针对全局数组m_pSlotData和m_nAlloc以及m_nMax这三个数据成员进行操作 int nAlloc = m_nAlloc;//m_pSlotData数组的长度
int nSlot = m_nRover;//假设当前所分配的槽的下一个槽未被使用(大多数情况下是这样的)

①if(nSlot >= nAlloc)/* || m_pSlotData[nSlot].dwFlags & SLOT_USED)*/
{//当nSlot达到值32时,表示从1号槽分配到了31号槽,if条件达到上限
②for(nSlot=1;nSlot<nAlloc && m_pSlotData[nSlot].dwFlags & SLOT_USED;nSlot++);
//当nSlot达到32时,有可能1-31之间的槽有可能被释放,所以用for循环搜索一遍。如果不存在空槽,申请更多的空间
③if(nSlot>=nAlloc)//如果for循环后nSlot的值仍然大于32,说明31前面的槽确实全部占满,需要申请空间
{
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);
memset(pSlotData+m_nAlloc,0,(nNewAlloc-nAlloc)*sizeof(CSlotData));
m_nAlloc=nNewAlloc;
m_pSlotData = pSlotData; }
}
④if(nSlot>=nAlloc)if(nSlot >= m_nMax)//如果搜索到的槽号大于槽的占用最大值,增加槽占用的最大值,反之最大值保持不变
m_nMax = nSlot +1;
m_pSlotData[nSlot].dwFlags |= SLOT_USED;
⑤m_nRover = nSlot + 1;
return nSlot;
}
当0-31号槽全部分配后,这32个槽全部处于占用状态,如果此时第5个槽被释放(FreeSlot(5)),接着,继续分配槽,AllocSlot()函数的调用流程将是这样的:
一、AllocSlot()调用开始,见代码中标为①的地方
①if(nSlot >= nAlloc) nSlot=32 ,m_nAlloc为32,此条件成立,进入该if语句内部
二、此时流程到代码中标注为②的地方
②for(nSlot=1;nSlot<nAlloc && m_pSlotData[nSlot].dwFlags & SLOT_USED;nSlot++);
这个for循环从1到32进行遍历,查找是否有空槽。因为第5号槽已经释放,for循环结束后,nSlot的值将是5
三、流程来到代码中标为③的地方
③if(nSlot>=nAlloc) 此时nSlot=5,nAlloc=32,条件为假跳出if语句块。
四、流程转到代码中④位置处
m_pSlotData[nSlot].dwFlags |= SLOT_USED;语句将把第5号槽置为使用状态,
接着的下一条语句m_nRover = nSlot + 1(代码中④位置处),将把m_nRover置为6
分析:这就意味着下一次分配槽时,将会分配第6号槽,注意:这个第6号槽处于使用状态,对于一个处于使用状态的槽,是不能再对它进行分配的。
为了验证我的猜想,本人仿照CThreadSlotData类的实现,模拟上述释放一个槽后再分配,结果真的出现了我所说的这个问题
源代码工作过程:
首先执行32次AllocSlot(),让0-31号槽全部处于占用状态
释放第5号槽位,再调用AllocSlot()进行槽位分配,此时m_nRover的值为5+1=6;第6槽是处于占用状态的。
接着再调用AllocSlot()分配槽时,问题出现了,处于占用状态的6号槽位被重新分配,即覆盖了。解决办法,
上述问题出现的原因是AllocSlot()函数m_nRover 的值没有得到正确的更新,在更新m_nRover的值时,应该用一个循环遍历动态数组,查找下一个空闲的槽,把下一个空闲的槽号赋给 m_nRover,这个问题就能得到解决。
即把m_nRover = nSlot + 1用一个for循环代替
for(int SlotNum=1;SlotNum<m_nAlloc && m_pSlotData[SlotNum].dwFlags & SLOT_USED;SlotNum++);
m_nRover = SlotNum;
_afxtsl.h代码如下:
#include "windows.h"
#ifndef __AFXTLS_H__
#define __AFXTLS_H__struct CSlotData
{
DWORD dwFlags;
HINSTANCE hInst;
};
class CThreadSlotData
{
public:
CThreadSlotData(); int AllocSlot();
void FreeSlot(int nSlot); int m_nAlloc;
int m_nRover;
int m_nMax;
CSlotData* m_pSlotData;
~CThreadSlotData();};
#endif
afxtls.cpp源代码如下:
#include "_afxtls.h"
#define SLOT_USED 0x01
CThreadSlotData::CThreadSlotData()
{ m_nMax = 0;
m_nRover = 1;
m_nAlloc = 0;
m_pSlotData = NULL;}int CThreadSlotData::AllocSlot()
{//此函数针对全局数组m_pSlotData和m_nAlloc以及m_nMax这三个数据成员进行操作
int nAlloc = m_nAlloc;//m_pSlotData数组的长度
int nSlot = m_nRover;//假设当前所分配的槽的下一个槽未被使用(大多数情况下是这样的)

if(nSlot >= nAlloc)/* || m_pSlotData[nSlot].dwFlags & SLOT_USED)*/
{//当nSlot达到值32时,表示从1号槽分配到了31号槽,if条件达到上限
for(nSlot=1;nSlot<nAlloc && m_pSlotData[nSlot].dwFlags & SLOT_USED;nSlot++);
//当nSlot达到32时,有可能1-31之间的槽有可能被释放,所以用for循环搜索一遍。如果不存在空槽,申请更多的空间
if(nSlot>=nAlloc)//如果for循环后nSlot的值仍然大于32,说明31前面的槽确实全部占满,需要申请空间
{
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);
memset(pSlotData+m_nAlloc,0,(nNewAlloc-nAlloc)*sizeof(CSlotData));
m_nAlloc=nNewAlloc;
m_pSlotData = pSlotData; } }
if(nSlot >= m_nMax)//如果搜索到的槽号大于槽的占用最大值,增加槽占用的最大值,反之最大值保持不变
m_nMax = nSlot +1;
m_pSlotData[nSlot].dwFlags |= SLOT_USED;
//m_nRover = nSlot + 1;
for(int SlotNum=1;SlotNum<m_nAlloc && m_pSlotData[SlotNum].dwFlags & SLOT_USED;SlotNum++);
m_nRover = SlotNum;
return nSlot;
}void CThreadSlotData::FreeSlot(int nSlot)
{ m_pSlotData[nSlot].dwFlags &= ~SLOT_USED;
}
CThreadSlotData::~CThreadSlotData()
{ if(m_pSlotData!=NULL)
{
HGLOBAL hSlotData=::GlobalHandle(m_pSlotData);
::GlobalUnlock(hSlotData);
::GlobalFree(hSlotData);
m_pSlotData = NULL; }
}
DynaArray源代码如下:
#include "_AFXTLS.H"
#include <stdio.h>int main()
{
CThreadSlotData* _afxThreadData =  new CThreadSlotData; for(int i=1;i<32;i++)
{ _afxThreadData->AllocSlot();
printf("m_pSlotData数组,槽%d,占用情况%x\n",i,_afxThreadData->m_pSlotData[i].dwFlags);
}
printf("m_nAlloc = %d,m_nMax = %d,m_nRover = %d\n",_afxThreadData->m_nAlloc,
_afxThreadData->m_nMax,_afxThreadData->m_nRover); _afxThreadData->FreeSlot(5);//释放第5个槽
_afxThreadData->FreeSlot(7);
_afxThreadData->FreeSlot(13);
_afxThreadData->FreeSlot(25);
_afxThreadData->FreeSlot(18);
_afxThreadData->FreeSlot(12);
for(i=1;i<32;i++)
{
printf("m_pSlotData数组,槽%d,占用情况%x\n",i,_afxThreadData->m_pSlotData[i].dwFlags);
}
printf("m_nAlloc = %d,m_nMax = %d,m_nRover = %d\n",_afxThreadData->m_nAlloc,
_afxThreadData->m_nMax,_afxThreadData->m_nRover);
int nSlot = _afxThreadData->AllocSlot();//继续分配
printf("现在分配的槽号是%d\n",nSlot);
printf("m_nAlloc = %d,m_nMax = %d,m_nRover = %d\n",_afxThreadData->m_nAlloc,
_afxThreadData->m_nMax,_afxThreadData->m_nRover);
nSlot = _afxThreadData->AllocSlot();
printf("现在分配的槽号是%d\n",nSlot);
printf("m_nAlloc = %d,m_nMax = %d,m_nRover = %d\n",_afxThreadData->m_nAlloc,
_afxThreadData->m_nMax,_afxThreadData->m_nRover);
delete _afxThreadData;
return 0;
}