关于《Windows程序设计(第2版)王艳平 张铮编著》第3章设计TLS里的一个问题

如摘要所述,经过断点后发现如下代码有问题:

CNoTrackObject* CThreadLocalObject::GetData(CNoTrackObject* (*pfnCreateObject)())
{
	if (m_nSlot == 0) {
		if (_afxThreadData == nullptr) {
			_afxThreadData = new (__afxThreadData) CThreadSlotData;
			//OutputDebugString(_T("New CThreadSlotData\n"));
		}
		m_nSlot = _afxThreadData->AllocSlot();
	}
        // ...
}

这里面_afxThreadData的new执行了多次,很明显是由于多个线程在上一行if判断_afxThreadData为nullptr后即被挂起,之后被调度执行时,出现这种情形:一个线程的AllocSlot已经调用过了,另一个线程跑过来将_afxThreadData又new了一遍,抹掉了之前AllocSlot里面做的各种关键操作,状态彻底混乱了。 于是,我尝试在afxtls.cpp里写了下面一个类来做这里的new操作:

class CTlsInitializer
{
public:
	CTlsInitializer() {
		if (_afxThreadData == nullptr) {
			_afxThreadData = new (__afxThreadData) CThreadSlotData;
		}
	}
};
CTlsInitializer gInitializer;

实践证明结果正确了,之后无论怎么运行程序都是正确的结果,release和debug都不崩溃。 后来想了想,其实只要这段new能无竞争的跑一遍,以后就不会有问题了,像上面单独弄一个CTlsInitializer gInitializer;虽然也可以,但其实只要在主线程里调用一下CThreadLocal的GetData函数,即可达到同样的目的。

CThreadLocal<MyThreadData1> g_myThreadData1;
CThreadLocal<MyThreadData2> g_myThreadData2;
// ...
int main() {
    //g_myThreadData1.operator->();
    (MyThreadData1*)g_myThreadData1; // 与上一行等价. 不用所有的CThreadLocal对象都做这个操作,只要任意一个即可。
    return 0;
}

运行效果和上面一样,也是正确的。不过从实用的角度,还是CTlsInitializer gInitializer;为好。

转载于:https://my.oschina.net/zhoubaojing/blog/715526

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值