詳解多執行緒MT和多執行緒MD的區別
區別1:全域性堆控制代碼不一樣。
網上有一個說法,就是一個執行緒一個棧,一個模組一個堆。前者很容易有理解,每個執行緒建立的時候在CreateThread中都能制定預設棧大小,只是很多情況下都取了預設值。而一個模組一個堆呢?其實很簡單測試,如果是一個多執行緒MT編譯方式的程式,你寫一個dll,匯出一個函式,引數設定為vector<int>,然後在exe中呼叫,當匯出函式結束時就會崩潰掉。其實原因很簡單,就是因為初始化向量空間時malloc記憶體的過程在exe中,而vector析構時會free記憶體,申請和釋放的模組不一致而違背了一個模組一個堆的說法。
細心者會發現,其實不管是new/delete還是malloc/free最終呼叫的都是HeapAlloc/HeapFree,而這個函式的第一個引數為一個全域性的堆控制代碼,由CreateHeap建立,建立該全域性堆控制代碼的尚且在main等系列主函式之前。事實上這種誇模組堆操作異常總結起來就是申請記憶體時HeapAlloc傳入的控制代碼和釋放該記憶體時HeapFree傳入的控制代碼不一致引起的,讀者可寫程式碼測試。
但是以上問題如果是多執行緒MD編譯方式下便可解決,也就是說如果都是通過多執行緒MD編譯方式出來的程式,如果是A模組中申請的記憶體到B模組中釋放不會出現問題。
區別2:連結的執行時庫不同。
對於多執行緒MT的程式來說,其連線的是libcmt.lib,該檔案屬於C語言執行時庫,整個lib都會連線到PE檔案當中。而多執行緒MD的程式連結的卻是類似msvcpXXX.dll,該檔案屬於微軟執行時庫.也就是說如果是多執行緒MD編譯出來的檔案執行時都會載入相應版本的執行時庫,當如果找不到執行時庫就會報錯而無法執行,同時如果執行時庫不匹配也會出現各種意料之外的崩潰或者程式根本跑不起來等情況。
區別3:編譯出來的PE檔案大小區別
此時如果兩者作為對比就會很明顯看到多執行緒MT編譯出來的檔案體積要比多執行緒MD編譯出來的大,因為MT是把對應的執行時庫直接放到編譯出來的PE檔案當中,而MD卻是執行的時候從第三方dll中獲取執行時庫,自己本身卻不包含。同時另外的區別也很明顯,多執行緒MT編譯出來的檔案執行時不需要載入第三方dll所以執行效率要比多執行緒MD稍微高一點點,當然作為使用者是完全感覺不到的。所以說如果開啟一個程式目錄,發現裡面有類似msvcrtXX.dll,那麼這個程式幾乎可以肯定是用多執行緒MD方式編譯的。
以上區別一言以蔽之就是多執行緒MT載入的是靜態執行時庫,屬於C語言版本;而多執行緒MD版本載入是動態執行時庫,屬於微軟版本。
總結:其實絕大多數軟體都是採用多執行緒MD方式編譯,例如QQ迅雷等等,如果找到他們目錄很容易發現上面提到的執行時庫。因為這樣一來編譯出來的檔案小,所有執行時庫統一,同時也讓記憶體管理簡單化,省去了跨模組記憶體訪問帶來的各種bug。