一文搞懂多線程中各個(gè)難點(diǎn)
2.3線程注意點(diǎn)
1、線程ID是進(jìn)程地址空間內(nèi)的一個(gè)地址, 要在同一個(gè)線程組內(nèi)進(jìn)行線程之間的比較才有意義。不同線程組內(nèi)的兩個(gè)線程, 哪怕兩者的pthread_t值是一樣的, 也不是同一個(gè)線程。
2、線程ID就有可能會(huì)被復(fù)用:
1、線程退出。
2、線程組的其他線程對(duì)該線程執(zhí)行了pthread_join, 或者線程退出前將分離狀態(tài)設(shè)置為已分離。
3、再次調(diào)用pthread_create創(chuàng)建線程。
2.4線程創(chuàng)建出來(lái)的默認(rèn)值
線程創(chuàng)建的第二個(gè)參數(shù)是pthread_attr_t類(lèi)型的指針, pthread_attr_init函數(shù)會(huì)將線程的屬性重置成默認(rèn)值。
線程屬性及默認(rèn)值:
如果確實(shí)需要很多的線程, 可以調(diào)用接口來(lái)調(diào)整線程棧的大小:
#include
線程終止,但進(jìn)程不會(huì)終止的方法:
1、入口函數(shù)的return返回,線程就退出了
2、線程調(diào)用pthread_exit(NULL),誰(shuí)調(diào)用誰(shuí)退出
#include
void pthread_exit(void *retval);
參數(shù):retval是返回信息,”臨終遺言“,可以給可以不給
該變量不能使用臨時(shí)變量。
可使用:全局變量、堆上開(kāi)辟的空間、字符串常量。
pthread_exit和線程啟動(dòng)函數(shù)(start_routine) 執(zhí)行return是有區(qū)別的。在start_routine中調(diào)用的任何層級(jí)的函數(shù)執(zhí)行pthread_exit() 都會(huì)引發(fā)線程退出, 而return, 只能是在start_routine函數(shù)內(nèi)執(zhí)行才能導(dǎo)致線程退出。
3、其它線程調(diào)用了pthread_cancel函數(shù)取消了該線程
int pthread_cancel(pthread_t thread);
thread:線程標(biāo)識(shí)符
調(diào)用該函數(shù)的執(zhí)行流可以取消其它線程,但是需要知道其它線程的線程標(biāo)識(shí)符,也可以執(zhí)行流自己取消自己,傳入自己的線程標(biāo)識(shí)符。
如果線程組中的任何一個(gè)線程調(diào)用了exit函數(shù), 或者主線程在main函數(shù)中執(zhí)行了return語(yǔ)句, 那么整個(gè)線程組內(nèi)的所有線程都會(huì)終止。
4.線程等待4.1線程等待接口#include
調(diào)用該函數(shù),該執(zhí)行流在等待線程退出的時(shí)候,該執(zhí)行流是阻塞在pthread_joind當(dāng)中的。
4.2線程等待和進(jìn)程等待的不同
第一點(diǎn)不同之處是進(jìn)程之間的等待只能是父進(jìn)程等待子進(jìn)程, 而線程則不然。線程組內(nèi)的成員是對(duì)等的關(guān)系, 只要是在一個(gè)線程組內(nèi), 就可以對(duì)另外一個(gè)線程執(zhí)行連接(join) 操作。
第二點(diǎn)不同之處是進(jìn)程可以等待任一子進(jìn)程的退出 , 但是線程的連接操作沒(méi)有類(lèi)似的接口, 即不能連接線程組內(nèi)的任一線程, 必須明確指明要連接的線程的線程ID。
pthread_join()錯(cuò)誤碼:
4.3為什么要等待退出的線程?
如果不連接已經(jīng)退出的線程, 會(huì)導(dǎo)致資源無(wú)法釋放。所謂資源指的又是什么呢?
1、已經(jīng)退出的線程, 其空間沒(méi)有被釋放, 仍然在進(jìn)程的地址空間之內(nèi)。
2、新創(chuàng)建的線程, 沒(méi)有復(fù)用剛才退出的線程的地址空間。
如果不執(zhí)行連接操作, 線程的資源就不能被釋放, 也不能被復(fù)用, 這就造成了資源的泄漏。
縱然調(diào)用了pthread_join, 也并沒(méi)有立即調(diào)用munmap來(lái)釋放掉退出線程的棧, 它們是被后建的線程復(fù)用了。釋放線程資源的時(shí)候, 若進(jìn)程可能再次創(chuàng)建線程, 而頻繁地munmap和mmap會(huì)影響性能, 所以將該棧緩存起來(lái), 放到一個(gè)鏈表之中, 如果有新的創(chuàng)建線程的請(qǐng)求, 會(huì)首先在棧緩存鏈表中尋找空間合適的棧, 有的話, 直接將該棧分配給新創(chuàng)建的線程。
例:
#include
默認(rèn)情況下, 新創(chuàng)建的線程處于可連接(Joinable) 的狀態(tài), 可連接狀態(tài)的線程退出后, 需要對(duì)其執(zhí)行連接操作, 否則線程資源無(wú)法釋放, 從而造成資源泄漏。
如果其他線程并不關(guān)心線程的返回值, 那么連接操作就會(huì)變成一種負(fù)擔(dān):你不需要它, 但是你不去執(zhí)行連接操作又會(huì)造成資源泄漏。這時(shí)候你需要的東西只是:線程退出時(shí), 系統(tǒng)自動(dòng)將線程相關(guān)的資源釋放掉, 無(wú)須等待連接。
可以是線程組內(nèi)其他線程對(duì)目標(biāo)線程進(jìn)行分離, 也可以是線程自己執(zhí)行pthread_detach函數(shù)。
線程的狀態(tài)之中, 可連接狀態(tài)和已分離狀態(tài)是沖突的, 一個(gè)線程不能既是可連接的, 又是已分離的。因此, 如果線程處于已分離的狀態(tài), 其他線程嘗試連接線程時(shí), 會(huì)返回EINVAL錯(cuò)誤。
pthread_detach錯(cuò)誤碼:
注意:這里的已分離不是指線程失去控制,不歸線程組管,而是指線程退出后,系統(tǒng)會(huì)自動(dòng)釋放線程資源。若是線程組內(nèi)的任意線程執(zhí)行了exit函數(shù),即使是已分離的線程,也仍會(huì)收到影響,一并退出。
6.線程安全
線程安全中涉及到的概念:
臨界資源:多線程中都能訪問(wèn)到的資源
臨界區(qū):每個(gè)線程內(nèi)部,訪問(wèn)臨界資源的代碼,就叫臨界區(qū)6.1什么是線程不安全?
多個(gè)線程訪問(wèn)同一塊臨界資源,導(dǎo)致資源產(chǎn)生二義性的現(xiàn)象。
6.1.1舉一個(gè)例子
假設(shè)現(xiàn)在有兩個(gè)線程A和B,單核CPU的情況下,此時(shí)有一個(gè)int類(lèi)型的全局變量為100,A和B的入口函數(shù)都要對(duì)這個(gè)全局變量進(jìn)行–操作。
線程A先拿到CPU資源后,對(duì)全局變量進(jìn)行–操作并不是原子性操作,也就是意味著,A在執(zhí)行–的過(guò)程中有可能會(huì)被打斷。假設(shè)A剛剛將全局變量的值讀到寄存器當(dāng)中,就被切換出去了,此時(shí)程序計(jì)數(shù)器保存了下一條執(zhí)行的指令,上下文信息保存寄存器中的值,這兩個(gè)東西是用來(lái)線程A再次拿到CPU資源后,恢復(fù)現(xiàn)場(chǎng)使用的。
此時(shí),線程B拿到了CPU資源,對(duì)全局變量進(jìn)行了–操作,并且將100減為了99,回寫(xiě)到了內(nèi)存中。
A再次擁有了CPU資源后,恢復(fù)現(xiàn)場(chǎng),繼續(xù)往下執(zhí)行,從寄存器中讀到的值仍為100,減完之后為99,回寫(xiě)到內(nèi)存中為99。
上述例子中,線程A和B都對(duì)全局變量進(jìn)行了–操作,全局變量的值應(yīng)該變?yōu)?8,但程序現(xiàn)在實(shí)際的結(jié)果為99,所以這就導(dǎo)致了線程不安全。
6.2如何解決線程不安全現(xiàn)象?
解決方案只需做到下述三點(diǎn)即可:
1、代碼必須要有互斥的行為:當(dāng)一個(gè)線程正在臨界區(qū)中執(zhí)行時(shí), 不允許其他線程進(jìn)入該臨界區(qū)中。
2、如果多個(gè)線程同時(shí)要求執(zhí)行臨界區(qū)的代碼, 并且當(dāng)前臨界區(qū)并沒(méi)有線程在執(zhí)行, 那么只能允許一個(gè)線程進(jìn)入該臨界區(qū)。
3、如果線程不在臨界區(qū)中執(zhí)行, 那么該線程不能阻止其他線程進(jìn)入臨界區(qū)。
則本質(zhì)上,我們需要對(duì)該臨界區(qū)加一把鎖:
鎖是一個(gè)很普遍的需求, 當(dāng)然用戶可以自行實(shí)現(xiàn)鎖來(lái)保護(hù)臨界區(qū)。但是實(shí)現(xiàn)一個(gè)正確并且高效的鎖非常困難?v然拋下高效不談, 讓用戶從零開(kāi)始實(shí)現(xiàn)一個(gè)正確的鎖也并不容易。正是因?yàn)檫@種需求具有普遍性, 所以Linux提供了互斥量。
6.3互斥量接口
6.3.1互斥量的初始化
1、靜態(tài)分配:
#include
2、動(dòng)態(tài)分配:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
調(diào)用int pthread_mutex_init()函數(shù)后,互斥量是處于沒(méi)有加鎖的狀態(tài)。
6.3.2互斥量的銷(xiāo)毀
int pthread_mutex_destroy(pthread_mutex_t *mutex);
注意:
1、使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量無(wú)須銷(xiāo)毀。
2、不要銷(xiāo)毀一個(gè)已加鎖的互斥量, 或者是真正配合條件變量使用的互斥量。
3、已經(jīng)銷(xiāo)毀的互斥量, 要確保后面不會(huì)有線程再嘗試加鎖。
當(dāng)互斥量處于已加鎖的狀態(tài), 或者正在和條件變量配合使用, 調(diào)用pthread_mutex_destroy函數(shù)會(huì)返回EBUSY錯(cuò)誤碼。
6.3.3互斥量的加鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
第一個(gè)接口:int pthread_mutex_lock(pthread_mutex_t *mutex);
1、該接口是阻塞加鎖接口。
2、mutex為傳入互斥鎖變量的地址
3、如果mutex當(dāng)中的計(jì)數(shù)器為1,pthread_mutex_lock接口就返回了,表示加鎖成功,同時(shí)計(jì)數(shù)器當(dāng)中的值會(huì)被更改為0.
4、如果mutex當(dāng)中的計(jì)數(shù)器為0,pthread_mutex_lock接口就阻塞了,pthread_mutex_lock接口沒(méi)有返回了,阻塞在函數(shù)內(nèi)部,直到加鎖成功
第二個(gè)接口:int pthread_mutex_trylock(pthread_mutex_t *mutex);
1、該接口為非阻塞接口
2、mutex中計(jì)數(shù)器為1時(shí),加鎖成功,計(jì)數(shù)器置為0,然后返回
3、mutex中計(jì)數(shù)器為0時(shí),加鎖失敗,但也會(huì)返回,此時(shí)加鎖是失敗狀態(tài),一定不要去訪問(wèn)臨界資源
4、非阻塞接口一般都需要搭配循環(huán)來(lái)使用。
第三個(gè)接口:int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);
1、帶有超時(shí)時(shí)間的加鎖接口
2、不能直接獲取互斥鎖的時(shí)候,會(huì)等待abs_timeout時(shí)間
3、如果在這個(gè)時(shí)間內(nèi)加鎖成功了,直接返回,不需要再繼續(xù)等待剩余的時(shí)間,并且表示加鎖成功
4、如果超出了該時(shí)間,也返回了,但是加鎖失敗了,需要循環(huán)加鎖
上述三個(gè)加鎖接口,第一個(gè)接口用的最多。

發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
最新活動(dòng)更多
-
6月20日立即下載>> 【白皮書(shū)】精準(zhǔn)測(cè)量 安全高效——福祿克光伏行業(yè)解決方案
-
7月3日立即報(bào)名>> 【在線會(huì)議】英飛凌新一代智能照明方案賦能綠色建筑與工業(yè)互聯(lián)
-
7月22-29日立即報(bào)名>> 【線下論壇】第三屆安富利汽車(chē)生態(tài)圈峰會(huì)
-
7.30-8.1火熱報(bào)名中>> 全數(shù)會(huì)2025(第六屆)機(jī)器人及智能工廠展
-
7月31日免費(fèi)預(yù)約>> OFweek 2025具身機(jī)器人動(dòng)力電池技術(shù)應(yīng)用大會(huì)
-
免費(fèi)參會(huì)立即報(bào)名>> 7月30日- 8月1日 2025全數(shù)會(huì)工業(yè)芯片與傳感儀表展
推薦專題
- 1 AI 眼鏡讓百萬(wàn) APP「集體失業(yè)」?
- 2 大廠紛紛入局,百度、阿里、字節(jié)搶奪Agent話語(yǔ)權(quán)
- 3 深度報(bào)告|中國(guó)AI產(chǎn)業(yè)正在崛起成全球力量,市場(chǎng)潛力和關(guān)鍵挑戰(zhàn)有哪些?
- 4 上海跑出80億超級(jí)獨(dú)角獸:獲上市公司戰(zhàn)投,干人形機(jī)器人
- 5 國(guó)家數(shù)據(jù)局局長(zhǎng)劉烈宏調(diào)研格創(chuàng)東智
- 6 一文看懂視覺(jué)語(yǔ)言動(dòng)作模型(VLA)及其應(yīng)用
- 7 下一代入口之戰(zhàn):大廠為何紛紛押注智能體?
- 8 百億AI芯片訂單,瘋狂傾銷(xiāo)中東?
- 9 Robotaxi新消息密集釋放,量產(chǎn)元年誰(shuí)在領(lǐng)跑?
- 10 格斗大賽出圈!人形機(jī)器人致命短板曝光:頭腦過(guò)于簡(jiǎn)單