在用visual studio進行界面編程時(如MFC),前台UI我們能夠通過MFC的消息循環機制實現。而對於后台的數據處理。我們可能會用到多線程來處理。
那么對於大多數人(尤其是我這樣的菜鳥),一個比較快捷的方法便是選擇MFC多線程:AfxBeginThread或者CreateThread來進建立多線程。當一兩個線程還是能夠得。當有3個或者3個以上的線程出現時,極可能出現內存泄漏。原因分析例如以下:
CWinThread的多線程不安全性:
由於 CWinThread 會調用_beginthreadex來初始化C執行時庫。而相同地,假設線程被強制終止(TerminateThread),由於 TerminateThread是不會去管 C執行時庫的,從而,導致部分和引用計數相關的C執行時數據的內存釋放出現故障。最典型的特征是,使用STL庫的靜態變量 內存回收將出錯,從而導致進程退出時誤報異常。
此外,假設AfxBeginThread頻繁進行回收和分配線程,假設不嚴格操作,也會導致崩潰。
VC6中,應該嚴格控制STL庫的使用,避免MFC庫和STL庫並存,否則,會有非常多問題。
原因:
AfxBeginThread在內部直接調用了CreateThread創建線程而不是c語言下推薦的beginthreadex函數,而這兩個函數是有差別的,主要是c執行庫的歷史遺留問題造成的。
在多線程環境中存在問題的C/C++執行期庫變量和函數包含errno、_doserrno、strtok、_wcstok、strerror、_strerror、tmpnam、tmpfile、asctime、_wasctime、gmtime、_ecvt和_fcvt等。若要使多線程C/C++程序可以正確地執行,必須創建一個數據結構,並將它與使用C/C++執行期庫函數的每一個線程關聯起來。當你調用C / C + +執行期庫時,這些函數必須知道查看調用線程的數據塊,這樣就不會對別的線程產生不良影響。
那么系統是否知道在創建新線程時分配該數據塊呢?回答是它不知道。系統根本不知道你得到的應用程序是用C/C++編寫的。也不知道你調用函數的線程本身是不安全的。問題在於你必須正確地進行全部的操作。
若要創建一個新線程。絕對不要調用操作系統的CreateThread函數。必須調用C/C++執行期庫函數_beginthreadex。
以下是關於_beginthreadex的一些要點:
每一個線程均獲得由C/C++執行期庫的堆棧分配的自己的tiddata內存結構。(tiddata結構位於Mtdll.h文件里的Visual C++源碼中)。傳遞給_beginthreadex的線程函數的地址保存在tiddata內存塊中。
傳遞給該函數的參數也保存在該數據塊中。
_beginthreadex確實從內部調用CreateThread,由於這是操作系統了解怎樣創建新線程的唯一方法。###能夠看出調用_beginthreadex時分配了額外的內存空間。
假設調用CreateThread,而不是調用C / C + +執行期庫的_beginthreadex來創建新線程,將會發生什么情況。當一個線程調用要求tiddata結構的C / C + +執行期庫函數時,將會發生以下的一些情況(大多數C / C + +執行期庫函數都是線程安全函數,不須要該結構)。
首
先。 C / C + +執行期庫函數試圖(通過調用TlsGetValue)獲取線程的數據塊的地址。
假設返回
NULL作為tiddata塊的地址。調用線程就不擁有與該地址相關的tiddata塊。這時,C / C + +執行期庫函數就在現場為調用線程分配一個tiddata塊,並對它進行初始化。然后該tiddata塊(通過TlsSetValue)與線程相關聯。
###_beginthreadex相相應的推出函數是_endthreadex,這個函數會釋放tiddata的內容。
---------------------------
假設你採用CreateThread創建線程。而你沒有使用上面所提到的那些特殊執行期庫的話,也是不會出現故障的。假設一定要用那些執行庫的話就最好調用_beginthreadex 這個api來創建線程,否則tiddata數據塊就無法撤銷,引起內存泄漏。
你能夠參照_beginthreadex源碼來進行理解。
引申閱讀:
關於_beginthreadex和CreateThread的差別