_beginthreadex与CreateThread区别与联系
關于這兩個函數(shù)的區(qū)別,可以參考《Windows 核心編程(第五版)》的第六章 "線程基礎",這篇文章的思想多數(shù)來源于此,我只是作了一些整理。
線程對于初學者還說可能覺得很高深,這可以理解。對于某些有經(jīng)驗的程序員來說,可能覺得又太簡單,我覺得如果認為線程很簡單的人,都是沒有理解線程,線程里面涉及的東西太多,包括內存,初始化,線程同步等。我打算以QA的形式來寫這篇文章。
Q:為什么書上說要以_beginthreadex來替代CreateThread?
A:好了,一直用API CreateThread來創(chuàng)建線程的同志們要注意了,你可能會說我一直用這個API來創(chuàng)建線程,工作剛剛的,一點問題都沒有。如果真是這樣的話,我只能說是你運氣太好了。在_beginthreadex的內部,它調用了CreateThread來創(chuàng)建線程,Windows始終用CreateThread來創(chuàng)建線程。在調用CreateThread之前,beginthreadex它做了很多初始化的工作,所以它比CreateThread創(chuàng)建的線程更加安全。
????Q:為什么要用兩個相同功能的函數(shù)來對待單線程和多線程序程序呢?
A:這里就有一定的歷史原因了,標準C語言的庫在是1970年左右發(fā)明的,而在那時候,線程的概念尚未出現(xiàn)在任何一個操作系統(tǒng)上。但是,線程畢竟是出現(xiàn)了,讓我們來看看下面這個例子,來說明以前的CRT為什么不支持多線程:
假如代碼是這種情況,當執(zhí)行到"system"這個函數(shù)之后,if之前,操作系統(tǒng)把當時CPU時鐘周期分配給另一個線程,而在另一個線程中正好使用了會設置errno(這是C語言的一個全局變量)的CRT函數(shù),于是問題就出現(xiàn)了。
所以早期的CRT函數(shù)是沒有考慮到多線程的,在多線程中還會出問題的CRT函數(shù)還有:strtok, _wcstok, strerror, _strerror, tmpnam, tmpfile, asctime, etc. 為了保證C和C+多線程應用程序正常運行,必須創(chuàng)建一個數(shù)據(jù)結構,并使之與使用了C/C+運行庫函數(shù)的每個線程相關聯(lián),然后在調用CRT函數(shù)時,那些函數(shù)必須知道去查找主調線程的數(shù)據(jù)塊,從而避免影響到其他線程。
那么,當系統(tǒng)創(chuàng)建線程時,它怎么知道要分配這個數(shù)據(jù)塊,又應該如何分配,它不知道,它也不知道你所調用的函數(shù)是否是線程安全,所以說,我們在創(chuàng)建新線程時,一定不要調用操作系統(tǒng)的CreateThread(Windows API)函數(shù),相反,我們始終應當調用CRT函數(shù)_beginthreadex,原型如下:
_beginthreadex與CreateThread的對數(shù)列表是一樣的,只是參數(shù)名與類型不同,因為CRT函數(shù)不應該依賴于Windows的數(shù)據(jù)類型,下面有一個宏,來將CreateThread函數(shù)替換成_beginthreadex:
注意,_beginthreadex函數(shù)只存在于CRT庫的多線程版本中,如果你的程序鏈接到一個CRT單線程版本中,那么程序在鏈接時就會報錯,所以在用VS開發(fā)時,要注意這一點。
VS里面設置如下圖所示:
Q:為什么說_beginthreadex就要比CreateThread更好,你是怎么知道的?
A:由于Microsoft 已經(jīng)為CRT函數(shù)提供了源碼,我們可以看到_beginthreadex到底比CreateThread多做了些什么事情,源碼在Program Files\Microsoft Visual Studio 8\VC\crt\src\Threadex.c中,可以找到_beginthreadex的實現(xiàn),這里是它的實現(xiàn):?
_beginthreadex的源碼? [cpp]?view plaincopy我們要明確幾點:
1)每個線程都有自己的專用的_tiddata內存塊,它是從C/C++的堆是分配出來的。
2)傳給_beginthreadex的線程處理函數(shù)地址(線程的回調函數(shù)地址)是存在_tiddata內存塊中的。
3)_beginthreadex內部的確調用了CreateThread來創(chuàng)建線程,這(CreateThread)是操作系統(tǒng)創(chuàng)建線程的唯一方式。
4)退出線程時調用_endthreadex,它內部調用了API ExitThread,它會釋放創(chuàng)建線程在堆上分配的內存_tiddata。
Q:我要怎么終止線程?
A:與_beginthreadex相對應的退出線程的函數(shù)是_endthreadex,CreateThread 對應 ExitThread,一般情況下我們不要調用這兩個函數(shù)來終止線程,最好是讓線程走完它的線程處理函數(shù),讓它自生自滅。如果要調用的話,最好調用_endthreadex,但一般不推薦。
OK, 目前為止你應該對誰更好些的問題有了深入的了解,但是為什么調用CreateThread的程序仍然可以經(jīng)年累月的正常運行呢?當線程調用一個需要 tiddata結構的CRT函數(shù)時(大多數(shù)CRT函數(shù)是線程安全的,并不需要該結構),首先CRT函數(shù)試圖獲取線程的數(shù)據(jù)塊的地址(通過調用 TlsGetValue),然后,如果返回NULL,說明調用線程沒有相關聯(lián)的tiddata塊,那么CRT函數(shù)馬上為調用線程分配并初始化一個 tiddata塊,并將該內存塊關聯(lián)到線程(通過TlsSetValue),這樣,該CRT函數(shù)以及其他CRT函數(shù)都可以使用該線程的tiddata塊了 (此即所謂"前人栽樹后人乘涼"了,_)。
當然,如果說你的線程運行的時候一直沒有問題是幾乎不可能的。事實上,的確有一些問題需要說說。如 果線程使用了CRT的signal函數(shù),整個進程都會被中止,因為結構化異常處理體尚未準備好。同樣,如果不調用_endthreadex來中止線程就會 造成內存泄漏,如果使用_beginthreadex,當然會容易想到_endthreadex,但如果你習慣了使用CreateThread,是否還會 想起_endthreadex,我表示極大的懷疑,而且CreateThread/_endthreadex的組合怎么看怎么讓人別扭。
不要忘記 開始的問題,接下來讓我們再來看看效率問題。CRT庫的多線程版本在某些函數(shù)里面放置了同步原語,比如malloc,為了保證堆不會被同時調用的 malloc函數(shù)破壞,這不可避免地會對效率造成影響,C/C++的哲學我們不應忘記,"決不為自己沒有用到的付出代價",自然,我們無權要求單線程程序 為多線程程序付出它們不該付出的代價,所以,開頭的問題也有了答案。
上面所說的都是靜態(tài)鏈接的CRT庫,而CRT庫的動態(tài)鏈接版本則被編寫得更加 通用,以便能夠被任何運行的程序和DLL共享。正是基于這個原因,這個版本的庫只存在多線程版本。因為CRT庫是以DLL形式提供的,程序和DLL不需要 包含CRT庫的任何代碼,自然尺寸也就更小。同時,如果Microsoft修正了CRT庫DLL中的Bug,程序也就自然受益了。
總結
首先,如果你調用_beginthreadex,你會獲得線程的句柄,句柄當然需要關閉,但_endthreadex并沒有這么做。通 常是調用_beginthreadex的線程(很可能是主線程)來調用CloseHandle關閉不再需要的新線程的句柄。其次,如果你使用CRT函數(shù), 你只需要使用_beginthreadex即可。如果不使用,那么你可以只使用CreateThread。同樣,如果只有一個線程(主線程)使用 CRT,你也可以使用CreateThread;如果新創(chuàng)建的線程不使用CRT,那么你也不需要_beginthreadex和多線程CRT。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的_beginthreadex与CreateThread区别与联系的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ POD(Plain Old Da
- 下一篇: C++ POD与结构体声明