當(dāng)前位置:首頁(yè) > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > Linux 的多線程編程的幾點(diǎn)注意事項(xiàng)
Linux 平臺(tái)上的多線程程序開(kāi)發(fā)相對(duì)應(yīng)其他平臺(tái)(比如 Windows)的多線程 API 有一些細(xì)微和隱晦的差別。不注意這些 Linux 上的一些開(kāi)發(fā)陷阱,常常會(huì)導(dǎo)致程序問(wèn)題不窮,死鎖不斷。本文中從 6 個(gè)方面總結(jié)出 Linux 多線程編程上的問(wèn)題,并分別引出相關(guān)改善的開(kāi)發(fā)經(jīng)驗(yàn),用以避免這些的陷阱。我們希望這些經(jīng)驗(yàn)可以幫助讀者們能更好更快的熟悉 Linux 平臺(tái)的多線程編程。
Linux 上線程開(kāi)發(fā) API 的概要介紹
多線程開(kāi)發(fā)在 Linux 平臺(tái)上已經(jīng)有成熟的 Pthread 庫(kù)支持。其涉及的多線程開(kāi)發(fā)的基本概念主要包含三點(diǎn):線程,互斥鎖,條件。其中,線程操作又分線程的創(chuàng)建,退出,等待 3 種;コ怄i則包括 4 種操作,分別是創(chuàng)建,銷毀,加鎖和解鎖。條件操作有 5 種操作:創(chuàng)建,銷毀,觸發(fā),廣播和等待。其他的一些線程擴(kuò)展概念,如信號(hào)燈等,都可以通過(guò)上面的三個(gè)基本元素的基本操作封裝出來(lái)。
線程,互斥鎖,條件在 Linux 平臺(tái)上對(duì)應(yīng)的 API 可以用表 1 歸納。為了方便熟悉 Windows 線程編程的讀者熟悉 Linux 多線程開(kāi)發(fā)的 API,我們?cè)诒碇型瑫r(shí)也列出 Windows SDK 庫(kù)中所對(duì)應(yīng)的 API 名稱。
表 1. 線程函數(shù)列表
對(duì)象 操作 Linux Pthread API Windows SDK 庫(kù)對(duì)應(yīng) API
線程 創(chuàng)建 pthread_create CreateThread
退出 pthread_exit ThreadExit
等待 pthread_join WaitForSingleObject
互斥鎖 創(chuàng)建 pthread_mutex_init CreateMutex
銷毀 pthread_mutex_destroy CloseHandle
加鎖 pthread_mutex_lock WaitForSingleObject
解鎖 pthread_mutex_unlock ReleaseMutex
條件 創(chuàng)建 pthread_cond_init CreateEvent
銷毀 pthread_cond_destroy CloseHandle
觸發(fā) pthread_cond_signal SetEvent
廣播 pthread_cond_broadcast SetEvent / ResetEvent
等待 pthread_cond_wait / pthread_cond_timedwait SingleObjectAndWait
多線程開(kāi)發(fā)在 Linux 平臺(tái)上已經(jīng)有成熟的 Pthread 庫(kù)支持。其涉及的多線程開(kāi)發(fā)的基本概念主要包含三點(diǎn):線程,互斥鎖,條件。其中,線程操作又分線程的創(chuàng)建,退出,等待 3 種。互斥鎖則包括 4 種操作,分別是創(chuàng)建,銷毀,加鎖和解鎖。條件操作有 5 種操作:創(chuàng)建,銷毀,觸發(fā),廣播和等待。其他的一些線程擴(kuò)展概念,如信號(hào)燈等,都可以通過(guò)上面的三個(gè)基本元素的基本操作封裝出來(lái)。
Linux 線程編程中的 6 條經(jīng)驗(yàn)
1、盡量設(shè)置 recursive 屬性以初始化 Linux 的互斥變量
互斥鎖是多線程編程中基本的概念,在開(kāi)發(fā)中被廣泛使用。其調(diào)用次序?qū)哟吻逦?jiǎn)單:建鎖,加鎖,解鎖,銷毀鎖。但是需要注意的是,與諸如 Windows 平臺(tái)的互斥變量不同,在默認(rèn)情況下,Linux 下的同一線程無(wú)法對(duì)同一互斥鎖進(jìn)行遞歸加速,否則將發(fā)生死鎖。
所謂遞歸加鎖,就是在同一線程中試圖對(duì)互斥鎖進(jìn)行兩次或兩次以上的行為。其場(chǎng)景在 Linux 平臺(tái)上的代碼可由清單 1 所示。
清單 1. Linux 重復(fù)對(duì)互斥鎖加鎖實(shí)例
// 通過(guò)默認(rèn)條件建鎖
pthread_mutex_t *theMutex = new pthread_mutex_t;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutex_init(theMutex,&attr);
pthread_mutexattr_destroy(&attr);
// 遞歸加鎖
pthread_mutex_lock (theMutex);
pthread_mutex_lock (theMutex);
pthread_mutex_unlock (theMutex);
pthread_mutex_unlock (theMutex);
在以上代碼場(chǎng)景中,問(wèn)題將出現(xiàn)在第二次加鎖操作。由于在默認(rèn)情況下,Linux 不允許同一線程遞歸加鎖,因此在第二次加鎖操作時(shí)線程將出現(xiàn)死鎖。
Linux 互斥變量這種奇怪的行為或許對(duì)于特定的某些場(chǎng)景會(huì)所有用處,但是對(duì)于大多數(shù)情況下看起來(lái)更像是程序的一個(gè) bug 。畢竟,在同一線程中對(duì)同一互斥鎖進(jìn)行遞歸加鎖在尤其是二次開(kāi)發(fā)中經(jīng)常會(huì)需要。
這個(gè)問(wèn)題與互斥鎖的中的默認(rèn) recursive 屬性有關(guān)。解決問(wèn)題的方法就是顯式地在互斥變量初始化時(shí)將設(shè)置起 recursive 屬性;诖耍陨洗a其實(shí)稍作修改就可以很好的運(yùn)行,只需要在初始化鎖的時(shí)候加設(shè)置一個(gè)屬性。請(qǐng)看清單 2 。
清單 2. 設(shè)置互斥鎖 recursive 屬性實(shí)例
pthread_mutexattr_init(&attr);
//設(shè)置 recursive 屬性
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE_NP);
pthread_mutex_init(theMutex,&attr);
因此,建議盡量設(shè)置 recursive 屬性以初始化 Linux 的互斥鎖,這樣既可以解決同一線程遞歸加鎖的問(wèn)題,又可以避免很多情況下死鎖的發(fā)生。這樣做還有一個(gè)額外的好處,就是可以讓 Windows 和 Linux 下讓鎖的表現(xiàn)統(tǒng)一。
2、注意 Linux 平臺(tái)上觸發(fā)條件變量的自動(dòng)復(fù)位問(wèn)題
條件變量的置位和復(fù)位有兩種常用模型:第一種模型是當(dāng)條件變量置位(signaled)以后,如果當(dāng)前沒(méi)有線程在等待,其狀態(tài)會(huì)保持為置位(signaled),直到有等待的線程進(jìn)入被觸發(fā),其狀態(tài)才會(huì)變?yōu)閺?fù)位(unsignaled),這種模型的采用以 Windows 平臺(tái)上的 Auto-set Event 為代表。其狀態(tài)變化如圖 1 所示:
圖 1. Windows 的條件變量狀態(tài)變化流程
第二種模型則是 Linux 平臺(tái)的 Pthread 所采用的模型,當(dāng)條件變量置位(signaled)以后,即使當(dāng)前沒(méi)有任何線程在等待,其狀態(tài)也會(huì)恢復(fù)為復(fù)位(unsignaled)狀態(tài)。其狀態(tài)變化如圖 2 所示:
圖 2. Linux 的條件變量狀態(tài)變化流程
具體來(lái)說(shuō),Linux 平臺(tái)上 Pthread 下的條件變量狀態(tài)變化模型是這樣工作的:調(diào)用 pthread_cond_signal() 釋放被條件阻塞的線程時(shí),無(wú)論存不存在被阻塞的線程,條件都將被重新復(fù)位,下一個(gè)被條件阻塞的線程將不受影響。而對(duì)于 Windows,當(dāng)調(diào)用 SetEvent 觸發(fā) Auto-reset 的 Event 條件時(shí),如果沒(méi)有被條件阻塞的線程,那么條件將維持在觸發(fā)狀態(tài),直到有新的線程被條件阻塞并被釋放為止。
這種差異性對(duì)于那些熟悉 Windows 平臺(tái)上的條件變量狀態(tài)模型而要開(kāi)發(fā) Linux 平臺(tái)上多線程的程序員來(lái)說(shuō)可能會(huì)造成意想不到的尷尬結(jié)果。試想要實(shí)現(xiàn)一個(gè)旅客坐出租車的程序:旅客在路邊等出租車,調(diào)用條件等待。出租車來(lái)了,將觸發(fā)條件,旅客停止等待并上車。一個(gè)出租車只能搭載一波乘客,于是我們使用單一觸發(fā)的條件變量。這個(gè)實(shí)現(xiàn)邏輯在第一個(gè)模型下即使出租車先到,也不會(huì)有什么問(wèn)題,其過(guò)程如圖 3 所示:
圖 3. 采用 Windows 條件變量模型的出租車實(shí)例流程
然而如果按照這個(gè)思路來(lái)在 Linux 上來(lái)實(shí)現(xiàn),代碼看起來(lái)可能是清單 3 這樣。
清單 3. Linux 出租車案例代碼實(shí)例
// 提示出租車到達(dá)的條件變量
pthread_cond_t taxiCond;
// 同步鎖
pthread_mutex_t taxiMutex;
// 旅客到達(dá)等待出租車
void * traveler_arrive(void * name) {
cout<< ” Traveler: ” <<(char *)name<< ” needs a taxi now! ” <<endl;
pthread_mutex_lock(&taxiMutex);
pthread_cond_wait (&taxiCond, &taxtMutex);
pthread_mutex_unlock (&taxtMutex);
cout<< ” Traveler: ” << (char *)name << ” now got a taxi! ” <<endl;
pthread_exit( (void *)0 );
}
// 出租車到達(dá)
void * taxi_arrive(void *name) {
cout<< ” Taxi ” <<(char *)name<< ” arrives. ” <<endl;
pthread_cond_signal(&taxtCond);
pthread_exit( (void *)0 );
}
void main() {
// 初始化
taxtCond= PTHREAD_COND_INITIALIZER;
taxtMutex= PTHREAD_MUTEX_INITIALIZER;
pthread_t thread;
pthread_attr_t threadAttr;
pthread_attr_init(&threadAttr);
pthread_create(&thread, & threadAttr, taxt_arrive, (void *)( ” Jack ” ));
sleep(1);
pthread_create(&thread, &threadAttr, traveler_arrive, (void *)( ” Susan ” ));
sleep(1);
pthread_create(&thread, &threadAttr, taxi_arrive, (void *)( ” Mike ” ));
sleep(1);
return 0;
}
好的,運(yùn)行一下,看看結(jié)果如清單 4 。
清單 4. 程序結(jié)果輸出
Taxi Jack arrives.
Traveler Susan needs a taxi now!
Taxi Mike arrives.
Traveler Susan now got a taxi.
其過(guò)程如圖 4 所示:
圖 4. 采用 Linux 條件變量模型的出租車實(shí)例流程
通過(guò)對(duì)比結(jié)果,你會(huì)發(fā)現(xiàn)同樣的邏輯,在 Linux 平臺(tái)上運(yùn)行的結(jié)果卻完全是兩樣。對(duì)于在 Windows 平臺(tái)上的模型一, Jack 開(kāi)著出租車到了站臺(tái),觸發(fā)條件變量。如果沒(méi)顧客,條件變量將維持觸發(fā)狀態(tài),也就是說(shuō) Jack 停下車在那里等著。直到 Susan 小姐來(lái)了站臺(tái),執(zhí)行等待條件來(lái)找出租車。 Susan 搭上 Jack 的出租車離開(kāi),同時(shí)條件變量被自動(dòng)復(fù)位。
但是到了 Linux 平臺(tái),問(wèn)題就來(lái)了,Jack 到了站臺(tái)一看沒(méi)人,觸發(fā)的條件變量被直接復(fù)位,于是 Jack 排在等待隊(duì)列里面。來(lái)遲一秒的 Susan 小姐到了站臺(tái)卻看不到在那里等待的 Jack,只能等待,直到 Mike 開(kāi)車趕到,重新觸發(fā)條件變量,Susan 才上了 Mike 的車。這對(duì)于在排隊(duì)系統(tǒng)前面的 Jack 是不公平的,而問(wèn)題癥結(jié)是在于 Linux 平臺(tái)上條件變量觸發(fā)的自動(dòng)復(fù)位引起的一個(gè) Bug 。
條件變量在 Linux 平臺(tái)上的這種模型很難說(shuō)好壞。但是在實(shí)際開(kāi)發(fā)中,我們可以對(duì)代碼稍加改進(jìn)就可以避免這種差異的發(fā)生。由于這種差異只發(fā)生在觸發(fā)沒(méi)有被線程等待在條件變量的時(shí)刻,因此我們只需要掌握好觸發(fā)的時(shí)機(jī)即可。簡(jiǎn)單的做法是增加一個(gè)計(jì)數(shù)器記錄等待線程的個(gè)數(shù),在決定觸發(fā)條件變量前檢查下該變量即可。改進(jìn)后 Linux 函數(shù)如清單 5 所示。
清單 5. Linux 出租車案例代碼實(shí)例
// 提示出租車到達(dá)的條件變量
pthread_cond_t taxiCond;
// 同步鎖
pthread_mutex_t taxiMutex;
// 旅客人數(shù),初始為 0
int travelerCount=0;
// 旅客到達(dá)等待出租車
void * traveler_arrive(void * name) {
cout<< ” Traveler: ” <<(char *)name<< ” needs a taxi now! ” <<endl;
pthread_mutex_lock(&taxiMutex);
// 提示旅客人數(shù)增加
travelerCount++;
pthread_cond_wait (&taxiCond, &taxiMutex);
pthread_mutex_unlock (&taxiMutex);
cout<< ” Traveler: ” << (char *)name << ” now got a taxi! ” <<endl;
pthread_exit( (void *)0 );
}
// 出租車到達(dá)
void * taxi_arrive(void *name)
{
cout<< ” Taxi ” <<(char *)name<< ” arrives. ” <<endl;
while(true)
{
pthread_mutex_lock(&taxiMutex);
// 當(dāng)發(fā)現(xiàn)已經(jīng)有旅客在等待時(shí),才觸發(fā)條件變量
if(travelerCount>0)
{
pthread_cond_signal(&taxtCond);
pthread_mutex_unlock (&taxiMutex);
break;
}
pthread_mutex_unlock (&taxiMutex);
}
pthread_exit( (void *)0 );
}
因此我們建議在 Linux 平臺(tái)上要出發(fā)條件變量之前要檢查是否有等待的線程,只有當(dāng)有線程在等待時(shí)才對(duì)條件變量進(jìn)行觸發(fā)。
4、注意條件返回時(shí)互斥鎖的解鎖問(wèn)題
在 Linux 調(diào)用 pthread_cond_wait 進(jìn)行條件變量等待操作時(shí),我們?cè)黾右粋(gè)互斥變量參數(shù)是必要的,這是為了避免線程間的競(jìng)爭(zhēng)和饑餓情況。但是當(dāng)條件等待返回時(shí)候,需要注意的是一定不要遺漏對(duì)互斥變量進(jìn)行解鎖。
Linux 平臺(tái)上的 pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 函數(shù)返回時(shí),互斥鎖 mutex 將處于鎖定狀態(tài)。因此之后如果需要對(duì)臨界區(qū)數(shù)據(jù)進(jìn)行重新訪問(wèn),則沒(méi)有必要 對(duì) mutex 就行重新加鎖。但是,隨之而來(lái)的問(wèn)題是,每次條件等待以后需要加入一步手動(dòng)的解鎖操作。正如前文中乘客等待出租車的 Linux 代碼如清單 6 所示:
清單 6. 條件變量返回后的解鎖實(shí)例
void * traveler_arrive(void * name) {
cout<< ” Traveler: ” <<(char *)name<< ” needs a taxi now! ” <<endl;
pthread_mutex_lock(&taxiMutex);
pthread_cond_wait (&taxiCond, &taxtMutex);
pthread_mutex_unlock (&taxtMutex);
cout<< ” Traveler: ” << (char *)name << ” now got a taxi! ” <<endl;
pthread_exit( (void *)0 );
}
這一點(diǎn)對(duì)于熟悉 Windows 平臺(tái)多線程開(kāi)發(fā)的開(kāi)發(fā)者來(lái)說(shuō)尤為重要。 Windows 上的 SignalObjectAndWait() 函數(shù)是常與 Linux 平臺(tái)上的 pthread_cond_wait() 函數(shù)被看作是跨平臺(tái)編程時(shí)的一對(duì)等價(jià)函數(shù)。但是需要注意的是,兩個(gè)函數(shù)退出時(shí)的狀態(tài)是不一樣的。在 Windows 平臺(tái)上,SignalObjectAndWait(HANDLE a, HANDLE b, …… ) 方法在調(diào)用結(jié)束返回時(shí)的狀態(tài)是 a 和 b 都是置位(signaled)狀態(tài),在普遍的使用方法中,a 經(jīng)常是一個(gè) Mutex 變量,在這種情況下,當(dāng)返回時(shí),Mutex a 處于解鎖狀態(tài)(signaled),Event b 處于置位狀態(tài)(signaled), 因此,對(duì)于 Mutex a 而言,我們不需要考慮解鎖的問(wèn)題。而且,在 SignalObjectAndWait() 之后,如果需要對(duì)臨界區(qū)數(shù)據(jù)進(jìn)行重新訪問(wèn),都需要調(diào)用 WaitForSingleObject() 重新加鎖。這一點(diǎn)剛好與 Linux 下的 pthread_cond_wait() 完全相反。
Linux 對(duì)于 Windows 的這一點(diǎn)額外解鎖的操作區(qū)別很重要,一定得牢記。否則從 Windows 移植到 Linux 上的條件等待操作一旦忘了結(jié)束后的解鎖操作,程序?qū)⒖隙〞?huì)發(fā)生死鎖。
5、等待的絕對(duì)時(shí)間問(wèn)題
超時(shí)是多線程編程中一個(gè)常見(jiàn)的概念。例如,當(dāng)你在 Linux 平臺(tái)下使用 pthread_cond_timedwait() 時(shí)就需要指定超時(shí)這個(gè)參數(shù),以便這個(gè) API 的調(diào)用者多只被阻塞指定的時(shí)間間隔。但是如果你是第一次使用這個(gè) API 時(shí),首先你需要了解的就是這個(gè) API 當(dāng)中超時(shí)參數(shù)的特殊性(就如本節(jié)標(biāo)題所提示的那樣)。我們首先來(lái)看一下這個(gè) API 的定義。 pthread_cond_timedwait() 定義請(qǐng)看清單 7 。
清單 7. pthread_cond_timedwait() 函數(shù)定義
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
參數(shù) abstime 在這里用來(lái)表示和超時(shí)時(shí)間相關(guān)的一個(gè)參數(shù),但是需要注意的是它所表示的是一個(gè)絕對(duì)時(shí)間,而不是一個(gè)時(shí)間間隔數(shù)值,只有當(dāng)系統(tǒng)的當(dāng)前時(shí)間達(dá)到或者超過(guò) abstime 所表示的時(shí)間時(shí),才會(huì)觸發(fā)超時(shí)事件。這對(duì)于擁有 Windows 平臺(tái)線程開(kāi)發(fā)經(jīng)驗(yàn)的人來(lái)說(shuō)可能尤為困惑。因?yàn)?Windows 平臺(tái)下所有的 API 等待參數(shù)(如 SignalObjectAndWait,等)都是相對(duì)時(shí)間,
假設(shè)我們指定相對(duì)的超時(shí)時(shí)間參數(shù)如 dwMilliseconds (單位毫秒)來(lái)調(diào)用和超時(shí)相關(guān)的函數(shù),這樣就需要將 dwMilliseconds 轉(zhuǎn)化為 Linux 下的絕對(duì)時(shí)間參數(shù) abstime 使用。常用的轉(zhuǎn)換方法如清單 8 所示:
清單 8. 相對(duì)時(shí)間到絕對(duì)時(shí)間轉(zhuǎn)換實(shí)例
/* get the current time */
struct timeval now;
gettimeofday(&now, NULL);
/* add the offset to get timeout value */
abstime ->tv_nsec = now.tv_usec * 1000 + (dwMilliseconds % 1000) * 1000000;
abstime ->tv_sec = now.tv_sec + dwMilliseconds / 1000;
Linux 的絕對(duì)時(shí)間看似簡(jiǎn)單明了,卻是開(kāi)發(fā)中一個(gè)非常隱晦的陷阱。而且一旦你忘了時(shí)間轉(zhuǎn)換,可以想象,等待你的錯(cuò)誤將是多么的令人頭疼:如果忘了把相對(duì)時(shí)間轉(zhuǎn)換成絕對(duì)時(shí)間,相當(dāng)于你告訴系統(tǒng)你所等待的超時(shí)時(shí)間是過(guò)去式的 1970 年 1 月 1 號(hào)某個(gè)時(shí)間段,于是操作系統(tǒng)毫不猶豫馬上送給你一個(gè) timeout 的返回值,然后你會(huì)舉著拳頭抱怨為什么另外一個(gè)同步線程耗時(shí)居然如此之久,并一頭扎進(jìn)尋找耗時(shí)原因的深淵里。
6、正確處理 Linux 平臺(tái)下的線程結(jié)束問(wèn)題
在 Linux 平臺(tái)下,當(dāng)處理線程結(jié)束時(shí)需要注意的一個(gè)問(wèn)題就是如何讓一個(gè)線程善始善終,讓其所占資源得到正確釋放。在 Linux 平臺(tái)默認(rèn)情況下,雖然各個(gè)線程之間是相互獨(dú)立的,一個(gè)線程的終止不會(huì)去通知或影響其他的線程。但是已經(jīng)終止的線程的資源并不會(huì)隨著線程的終止而得到釋放,我們需要調(diào)用 pthread_join() 來(lái)獲得另一個(gè)線程的終止?fàn)顟B(tài)并且釋放該線程所占的資源。 Pthread_join() 函數(shù)的定義如清單 9 。
清單 9. pthread_join 函數(shù)定義
int pthread_join(pthread_t th, void **thread_return);
調(diào)用該函數(shù)的線程將掛起,等待 th 所表示的線程的結(jié)束。 thread_return 是指向線程 th 返回值的指針。需要注意的是 th 所表示的線程必須是 joinable 的,即處于非 detached(游離)狀態(tài);并且只可以有唯一的一個(gè)線程對(duì) th 調(diào)用 pthread_join() 。如果 th 處于 detached 狀態(tài),那么對(duì) th 的 pthread_join() 調(diào)用將返回錯(cuò)誤。
如果你壓根兒不關(guān)心一個(gè)線程的結(jié)束狀態(tài),那么也可以將一個(gè)線程設(shè)置為 detached 狀態(tài),從而來(lái)讓操作系統(tǒng)在該線程結(jié)束時(shí)來(lái)回收它所占的資源。將一個(gè)線程設(shè)置為 detached 狀態(tài)可以通過(guò)兩種方式來(lái)實(shí)現(xiàn)。一種是調(diào)用 pthread_detach() 函數(shù),可以將線程 th 設(shè)置為 detached 狀態(tài)。其申明如清單 10 。
清單 10. pthread_detach 函數(shù)定義
int pthread_detach(pthread_t th);
另一種方法是在創(chuàng)建線程時(shí)就將它設(shè)置為 detached 狀態(tài),首先初始化一個(gè)線程屬性變量,然后將其設(shè)置為 detached 狀態(tài),后將它作為參數(shù)傳入線程創(chuàng)建函數(shù) pthread_create(),這樣所創(chuàng)建出來(lái)的線程就直接處于 detached 狀態(tài)。方法如清單 11 。
清單 11. 創(chuàng)建 detach 線程代碼實(shí)例
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, THREAD_FUNCTION, arg);
總之為了在使用 Pthread 時(shí)避免線程的資源在線程結(jié)束時(shí)不能得到正確釋放,從而避免產(chǎn)生潛在的內(nèi)存泄漏問(wèn)題,在對(duì)待線程結(jié)束時(shí),要確保該線程處于 detached 狀態(tài),否著就需要調(diào)用 pthread_join() 函數(shù)來(lái)對(duì)其進(jìn)行資源回收。
總結(jié)與補(bǔ)充
本文以上部分詳細(xì)介紹了 Linux 的多線程編程的 5 條高效開(kāi)發(fā)經(jīng)驗(yàn)。另外你也可以考慮嘗試其他一些開(kāi)源類庫(kù)來(lái)進(jìn)行線程開(kāi)發(fā)。
1. Boost 庫(kù)
Boost 庫(kù)來(lái)自于由 C++ 標(biāo)準(zhǔn)委員會(huì)類庫(kù)工作組成員發(fā)起,致力于為 C++ 開(kāi)發(fā)新的類庫(kù)的 Boost 組織。雖然該庫(kù)本身并不是針對(duì)多線程而產(chǎn)生,但是發(fā)展至今,其已提供了比較全面的多線程編程的 API 支持。 Boost 庫(kù)對(duì)于多線程支持的 API 風(fēng)格上更類似于 Linux 的 Pthread 庫(kù),差別在于其將線程,互斥鎖,條件等線程開(kāi)發(fā)概念都封裝成了 C++ 類,以方便開(kāi)發(fā)調(diào)用。 Boost 庫(kù)目前對(duì)跨平臺(tái)支持的很不錯(cuò),不僅支持 Windows 和 Linux ,還支持各種商用的 Unix 版本。如果開(kāi)發(fā)者想使用高穩(wěn)定性的統(tǒng)一線程編程接口減輕跨平臺(tái)開(kāi)發(fā)的難度, Boost 庫(kù)將是首選。
2. ACE
ACE 全稱是 ADAPTIVE Communication Environment,它是一個(gè)免費(fèi)的,開(kāi)源的,面向?qū)ο蟮墓ぞ呖蚣,用以開(kāi)發(fā)并發(fā)訪問(wèn)的軟件。由于 ACE 初是面向網(wǎng)絡(luò)服務(wù)端的編程開(kāi)發(fā),因此對(duì)于線程開(kāi)發(fā)的工具庫(kù)它也能提供很全面的支持。其支持的平臺(tái)也很全面,包括 Windows,Linux 和各種版本 Unix 。 ACE 的唯一問(wèn)題是如果僅僅是用于線程編程,其似乎顯得有些過(guò)于重量級(jí)。而且其較復(fù)雜的配置也讓其部署對(duì)初學(xué)者而言并非易事。