Linux线程(三)
Linux線程(三)
文章目錄
- Linux線程(三)
- 一、互斥量
- 二、.互斥量的接口
- 三、互斥量實現(xiàn)用原理探究
- 四、可重入VS線程安全
- 五、死鎖
一、互斥量
根據(jù)前面的分析,得到的結(jié)果不是我們想要的原因是–ticket操作不是原子操作,這個共享資源可能并發(fā)的切換大其他線程,導(dǎo)致有多個線程同時影響到這個共享資源,所以導(dǎo)致得到的結(jié)果不對。
- 1.解決方法(加鎖—>Linux中叫這把鎖為互斥量):
- 代碼必須有互斥行為:當有一個執(zhí)行流(有一個線程)進入臨界區(qū)時,不允許其他線程進入該臨界區(qū)
- 如果多個線程同時要求執(zhí)行臨界區(qū)的代碼,并且臨界區(qū)內(nèi)沒有線程在執(zhí)行,那么只允許一個線程進入該臨界區(qū)
- 如果線程不在臨界區(qū)內(nèi)執(zhí)行,那么該線程不能阻止其他線程進入臨界區(qū)
二、.互斥量的接口
- 1.初始化互斥量
- 方法一:靜態(tài)分配
- 方法二:動態(tài)分配
- 功能:初始化互斥量
- 參數(shù):
- 返回值:成功返回0,失敗返回錯誤碼
- 2.銷毀互斥量:
- 功能:是銷毀一個互斥量
- 參數(shù):要銷毀的互斥量
- 注意??:使用PTHREAD_MUTEX_INITIALIZER(靜態(tài)初始化的互斥量)的互斥量不需要銷毀
- 不要銷毀一個已經(jīng)加鎖的互斥量(即銷毀前要釋放鎖)
- 已經(jīng)銷毀的互斥量,要確保后面不會有線程去嘗試對其加鎖
- 3.對互斥量加鎖和解鎖
調(diào)用pthread_mutex_lock可能會遇到以下幾種情況
-
互斥量處于未鎖狀態(tài),該函數(shù)將對該互斥量加鎖,同時返回成功
-
發(fā)起調(diào)用pthread_mutex_lock函數(shù)時,其他線程已經(jīng)把互斥量加完鎖,或者存在其他線程同時申請對同一個互斥量進行加鎖,但自己沒有成功競爭到互斥量,那么pthread_mutex_lock調(diào)用就會陷入阻塞(即執(zhí)行流被掛起),直到成功競爭到互斥量的線程釋放鎖,該執(zhí)行流才可能解除阻塞
-
注意:互斥鎖pthread_mutex也叫“掛起等待鎖”,一旦線程獲取鎖失敗,就會掛起到操作系統(tǒng)的一個等待隊列中,這個獲取鎖失敗而進入掛起等待隊列的線程具體什么時候恢復(fù)執(zhí)行也是不一定的,也不是獲取到鎖的線程釋放鎖后立即能執(zhí)行的,因為線程的萬惡之源”==搶占式==“。
-
4.改進前面的代碼
從上面的結(jié)果看現(xiàn)在的代碼已經(jīng)是正確的了。
- 5.互斥鎖的缺陷:
- 互斥鎖雖然能夠保證線程安全,但是最終導(dǎo)致程序的效率會受到影響,并且還有產(chǎn)生死鎖的風(fēng)險
- 產(chǎn)生死鎖的場景:a.一個線程進行加鎖后,再次嘗試對同一個臨界資源加鎖 b.多個線程多把鎖極易產(chǎn)生死鎖
三、互斥量實現(xiàn)用原理探究
- 經(jīng)過上面的例子,我們已經(jīng)知道單純的++ / – 操作都不是原子的,有可能會導(dǎo)致數(shù)據(jù)一致性問題
- 為了實現(xiàn)互斥鎖操作,大多數(shù)體系都提供了swap或exchange指令,該指令的作用是把寄存器和內(nèi)存單元的數(shù)據(jù)進行交換,由于只有一條指令,保證了原子性,即使是多處理器平臺,訪問內(nèi)存的總線周期也有先后,一個處理器上的交換指令執(zhí)行時也只能等待另一個處理器的交換指令總線周期執(zhí)行結(jié)束。
四、可重入VS線程安全
- 1.比較
| 重入是指同一個函數(shù)被不同的執(zhí)行流調(diào)用時,前一個執(zhí)行流的流程還未結(jié)束,就有其他的執(zhí)行流再次進入函數(shù)。一個函數(shù)在從重的情況下,運行結(jié)果不會出現(xiàn)任何不同或者沒有任何執(zhí)行問題,則稱該函數(shù)為可重入函數(shù),反之為不可重入函數(shù) | 線程安全是指多個線程并發(fā)執(zhí)行同一段代碼,不會出現(xiàn)不同的結(jié)果或不會出現(xiàn)執(zhí)行問題。其中如果對全局變量或?qū)o態(tài)變量進行操作,并且沒有鎖的保護的情況下,就有可能出現(xiàn)線程安全問題 |
注意:??:可重入包含了線程安全問題。即如果一個函數(shù)可重入就一定線程安全,反之如果一個函數(shù)是線程安全的就不一定是可重入的
- 2.常見的線程不安全的情況:
- 不保護共享變量的函數(shù)
- 函數(shù)狀態(tài)隨著被調(diào)用,狀態(tài)發(fā)生變化的函數(shù)
- 返回指向靜態(tài)變量指針的函數(shù)
- 調(diào)用線程不安全函數(shù)的函數(shù)
- 3.常見的線程安全的情況:
- 每個線程對全局變量或靜態(tài)變量只有讀取的權(quán)限而沒有寫入的權(quán)限,一般來說這些線程是線程安全的
- 類或者接口對于線程來說都是原子操作
- 多個線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果出現(xiàn)二義性
- 4.常見的不可重入的情況:
- 調(diào)用了malloc/free函數(shù),因為malloc是用全局鏈來管理堆的
- 調(diào)用了標準IO庫函數(shù),標準IO庫的很多實現(xiàn)都以不可重入的方式使用全局數(shù)據(jù)結(jié)構(gòu)的
- 可重入函數(shù)體內(nèi)使用了靜態(tài)的數(shù)據(jù)結(jié)構(gòu)
- 5.常見可重入情況:
- 不使用全局變量或靜態(tài)變量
- 不使用malloc或者new開辟出的空間
- 不調(diào)用不可重入函數(shù)
- 不返回靜態(tài)或全局數(shù)據(jù),所有的數(shù)據(jù)都有函數(shù)的調(diào)用者提供
- 使用本地數(shù)據(jù),或者使用通過制作全局數(shù)據(jù)的本地拷貝來保護全局數(shù)據(jù)
- 6.可重入與線程安全的聯(lián)系:
- 函數(shù)是可重入的就是線程安全的
- 函數(shù)是不可重入的,有可能會有線程安全問題
- 如果一個函數(shù)內(nèi)有全局變量/靜態(tài)數(shù)據(jù)等,那么這個函數(shù)既不是線程安全的也不是可重入的
- 7.可重入與線程安全的區(qū)別:
- 可重入函數(shù)是線程安全函數(shù)的一種
- 線程安全不一定可重入,而可重入函數(shù)一定是線程安全函數(shù)
- 如果將對臨界資源的訪問加上鎖,這這個函數(shù)是線程安全的,但如果這個重入函數(shù)若鎖還未釋放則會產(chǎn)生死鎖,因此是不可重入的
五、死鎖
- 1.死鎖:死鎖是指在一組進程中的各個進程均占有不會釋放的資源,但因為互相申請被其他進程所占用的不會被釋放的資源而處于一種永久等待的狀態(tài)
- 2,產(chǎn)生死鎖的四個必要條件
- 互斥條件:一個資源每次只能被一個執(zhí)行流使用
- 請求與保持的條件:一個執(zhí)行流因請求資源而阻塞時,要對已獲得資源的進程進行請求保持不放
- 不剝奪條件:一個執(zhí)行流已獲得的資源,在未使用完之前,不能強行剝奪
- 循環(huán)等待條件:若干個執(zhí)行流之間形成一種頭尾相接的循環(huán)等待資源的關(guān)系
- 3.避免死鎖
- 加鎖的順序要一致
- 避免鎖未釋放的場景
- 資源一次性分配
- 讓臨界區(qū)代碼盡可能的短一些
- 讓臨界區(qū)代碼不要調(diào)用復(fù)雜的函數(shù)
- 讓臨界區(qū)的代碼盡量快的執(zhí)行結(jié)束
- 4.避免死鎖的算法
- 死鎖檢查算法
- 銀行家算法
總結(jié)
以上是生活随笔為你收集整理的Linux线程(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux线程(二)
- 下一篇: Linux线程(四)