一文搞懂多線程中各個(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)建出來的默認(rèn)值
線程創(chuàng)建的第二個(gè)參數(shù)是pthread_attr_t類型的指針, pthread_attr_init函數(shù)會(huì)將線程的屬性重置成默認(rèn)值。
線程屬性及默認(rèn)值:
如果確實(shí)需要很多的線程, 可以調(diào)用接口來調(diào)整線程棧的大小:
#include
線程終止,但進(jìn)程不會(huì)終止的方法:
1、入口函數(shù)的return返回,線程就退出了
2、線程調(diào)用pthread_exit(NULL),誰調(diào)用誰退出
#include
void pthread_exit(void *retval);
參數(shù):retval是返回信息,”臨終遺言“,可以給可以不給
該變量不能使用臨時(shí)變量。
可使用:全局變量、堆上開辟的空間、字符串常量。
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語句, 那么整個(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)程的退出 , 但是線程的連接操作沒有類似的接口, 即不能連接線程組內(nèi)的任一線程, 必須明確指明要連接的線程的線程ID。
pthread_join()錯(cuò)誤碼:
4.3為什么要等待退出的線程?
如果不連接已經(jīng)退出的線程, 會(huì)導(dǎo)致資源無法釋放。所謂資源指的又是什么呢?
1、已經(jīng)退出的線程, 其空間沒有被釋放, 仍然在進(jìn)程的地址空間之內(nèi)。
2、新創(chuàng)建的線程, 沒有復(fù)用剛才退出的線程的地址空間。
如果不執(zhí)行連接操作, 線程的資源就不能被釋放, 也不能被復(fù)用, 這就造成了資源的泄漏。
縱然調(diào)用了pthread_join, 也并沒有立即調(diào)用munmap來釋放掉退出線程的棧, 它們是被后建的線程復(fù)用了。釋放線程資源的時(shí)候, 若進(jìn)程可能再次創(chuàng)建線程, 而頻繁地munmap和mmap會(huì)影響性能, 所以將該棧緩存起來, 放到一個(gè)鏈表之中, 如果有新的創(chuàng)建線程的請(qǐng)求, 會(huì)首先在棧緩存鏈表中尋找空間合適的棧, 有的話, 直接將該棧分配給新創(chuàng)建的線程。
例:
#include
默認(rèn)情況下, 新創(chuàng)建的線程處于可連接(Joinable) 的狀態(tài), 可連接狀態(tài)的線程退出后, 需要對(duì)其執(zhí)行連接操作, 否則線程資源無法釋放, 從而造成資源泄漏。
如果其他線程并不關(guān)心線程的返回值, 那么連接操作就會(huì)變成一種負(fù)擔(dān):你不需要它, 但是你不去執(zhí)行連接操作又會(huì)造成資源泄漏。這時(shí)候你需要的東西只是:線程退出時(shí), 系統(tǒng)自動(dòng)將線程相關(guān)的資源釋放掉, 無須等待連接。
可以是線程組內(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.線程安全
線程安全中涉及到的概念:
臨界資源:多線程中都能訪問到的資源
臨界區(qū):每個(gè)線程內(nèi)部,訪問臨界資源的代碼,就叫臨界區(qū)6.1什么是線程不安全?
多個(gè)線程訪問同一塊臨界資源,導(dǎo)致資源產(chǎn)生二義性的現(xiàn)象。
6.1.1舉一個(gè)例子
假設(shè)現(xiàn)在有兩個(gè)線程A和B,單核CPU的情況下,此時(shí)有一個(gè)int類型的全局變量為100,A和B的入口函數(shù)都要對(duì)這個(gè)全局變量進(jìn)行–操作。
線程A先拿到CPU資源后,對(duì)全局變量進(jìn)行–操作并不是原子性操作,也就是意味著,A在執(zhí)行–的過程中有可能會(huì)被打斷。假設(shè)A剛剛將全局變量的值讀到寄存器當(dāng)中,就被切換出去了,此時(shí)程序計(jì)數(shù)器保存了下一條執(zhí)行的指令,上下文信息保存寄存器中的值,這兩個(gè)東西是用來線程A再次拿到CPU資源后,恢復(fù)現(xiàn)場(chǎng)使用的。
此時(shí),線程B拿到了CPU資源,對(duì)全局變量進(jìn)行了–操作,并且將100減為了99,回寫到了內(nèi)存中。
A再次擁有了CPU資源后,恢復(fù)現(xiàn)場(chǎng),繼續(xù)往下執(zhí)行,從寄存器中讀到的值仍為100,減完之后為99,回寫到內(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ū)并沒有線程在執(zhí)行, 那么只能允許一個(gè)線程進(jìn)入該臨界區(qū)。
3、如果線程不在臨界區(qū)中執(zhí)行, 那么該線程不能阻止其他線程進(jìn)入臨界區(qū)。
則本質(zhì)上,我們需要對(duì)該臨界區(qū)加一把鎖:
鎖是一個(gè)很普遍的需求, 當(dāng)然用戶可以自行實(shí)現(xiàn)鎖來保護(hù)臨界區(qū)。但是實(shí)現(xiàn)一個(gè)正確并且高效的鎖非常困難。縱然拋下高效不談, 讓用戶從零開始實(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ù)后,互斥量是處于沒有加鎖的狀態(tài)。
6.3.2互斥量的銷毀
int pthread_mutex_destroy(pthread_mutex_t *mutex);
注意:
1、使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量無須銷毀。
2、不要銷毀一個(gè)已加鎖的互斥量, 或者是真正配合條件變量使用的互斥量。
3、已經(jīng)銷毀的互斥量, 要確保后面不會(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接口沒有返回了,阻塞在函數(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),一定不要去訪問臨界資源
4、非阻塞接口一般都需要搭配循環(huán)來使用。
第三個(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)更多
-
即日-11.13立即報(bào)名>>> 【在線會(huì)議】多物理場(chǎng)仿真助跑新能源汽車
-
11月28日立即報(bào)名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會(huì)議
-
12月19日立即報(bào)名>> 【線下會(huì)議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會(huì)
-
即日-12.26火熱報(bào)名中>> OFweek2024中國(guó)智造CIO在線峰會(huì)
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書》
-
精彩回顧立即查看>> 【限時(shí)免費(fèi)下載】TE暖通空調(diào)系統(tǒng)高效可靠的組件解決方案
推薦專題
- 1 【一周車話】沒有方向盤和踏板的車,你敢坐嗎?
- 2 特斯拉發(fā)布無人駕駛車,還未迎來“Chatgpt時(shí)刻”
- 3 特斯拉股價(jià)大跌15%:Robotaxi離落地還差一個(gè)蘿卜快跑
- 4 馬斯克給的“驚喜”夠嗎?
- 5 打完“價(jià)格戰(zhàn)”,大模型還要比什么?
- 6 馬斯克致敬“國(guó)產(chǎn)蘿卜”?
- 7 神經(jīng)網(wǎng)絡(luò),誰是盈利最強(qiáng)企業(yè)?
- 8 比蘋果偉大100倍!真正改寫人類歷史的智能產(chǎn)品降臨
- 9 諾獎(jiǎng)進(jìn)入“AI時(shí)代”,人類何去何從?
- 10 Open AI融資后成萬億獨(dú)角獸,AI人才之爭(zhēng)開啟
- 高級(jí)軟件工程師 廣東省/深圳市
- 自動(dòng)化高級(jí)工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級(jí)銷售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專家 廣東省/江門市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市