關於《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;爲好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章