如摘要所述,經過斷點後發現如下代碼有問題:
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;
爲好。