訂閱
糾錯(cuò)
加入自媒體

一文搞懂多線程中各個(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è)接口用的最多。

<上一頁(yè)  1  2  3  4  下一頁(yè)>  
聲明: 本文由入駐維科號(hào)的作者撰寫,觀點(diǎn)僅代表作者本人,不代表OFweek立場(chǎng)。如有侵權(quán)或其他問題,請(qǐng)聯(lián)系舉報(bào)。

發(fā)表評(píng)論

0條評(píng)論,0人參與

請(qǐng)輸入評(píng)論內(nèi)容...

請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字

您提交的評(píng)論過于頻繁,請(qǐng)輸入驗(yàn)證碼繼續(xù)

  • 看不清,點(diǎn)擊換一張  刷新

暫無評(píng)論

暫無評(píng)論

人工智能 獵頭職位 更多
掃碼關(guān)注公眾號(hào)
OFweek人工智能網(wǎng)
獲取更多精彩內(nèi)容
文章糾錯(cuò)
x
*文字標(biāo)題:
*糾錯(cuò)內(nèi)容:
聯(lián)系郵箱:
*驗(yàn) 證 碼:

粵公網(wǎng)安備 44030502002758號(hào)