多執行緒與多程序 二 執行緒優先順序與執行緒安全

2021-08-10 04:18:33 字數 2765 閱讀 2372

原文:

《程式設計思想之多執行緒與多程序(1)——以作業系統的角度述說執行緒與程序》一文詳細講述了執行緒、程序的關係及在作業系統中的表現,這是多執行緒學習必須了解的基礎。本文將接著講一下執行緒優先順序和執行緒安全。

現在主流作業系統(如windows、linux、mac os x)的任務排程除了具有前面提到的時間片輪轉的特點外,還有優先順序排程(priority schedule)的特點。優先順序排程決定了執行緒按照什麼順序輪流執行,在具有優先順序排程的系統中,執行緒擁有各自的執行緒優先順序(thread priority)。具有高優先順序的執行緒會更早地執行,而低優先順序的執行緒通常要等沒有更高優先順序的可執行執行緒時才會被執行。

執行緒的優先順序可以由使用者手動設定,此外系統也會根據不同情形調整優先順序。通常情況下,頻繁地進入等待狀態(進入等待狀態會放棄之前仍可占用的時間份額)的執行緒(如io執行緒),比頻繁進行大量計算以至於每次都把所有時間片全部用盡的執行緒更受作業系統的歡迎。因為頻繁進入等待的執行緒只會占用很少的時間,這樣作業系統可以處理更多的任務。我們把頻繁等待的執行緒稱之為io密集型執行緒(io bound thread),而把很少等待的執行緒稱之為cpu密集型執行緒(cpu bound thread)。io密集型執行緒總是比cpu密集型執行緒更容易得到優先順序的提公升。

在優先順序排程下,容易出現一種執行緒餓死的現象。乙個執行緒餓死是說它的優先順序較低,在它執行之前總是有比它優先順序更高的執行緒等待執行,因此這個低優先順序的執行緒始終得不到執行。當cpu密集型的執行緒優先順序較高時,其它低優先順序的執行緒就很可能出現餓死的情況;當io密集型執行緒優先順序較高時,其它執行緒相對不容易造成餓死的善,因為io執行緒有大量的等待時間。為了避免執行緒餓死,排程系統通常會逐步提公升那些等待了很久而得不到執行的執行緒的優先順序。這樣,乙個執行緒只要它等待了足夠長的時間,其優先順序總會被提公升到可以讓它執行的程度,也就是說這種情況下執行緒始終會得到執行,只是時間的問題。

在優先順序排程環境下,執行緒優先順序的改變有三種方式: 

1. 使用者指定優先順序; 

2. 根據進入等待狀態的頻繁程度提公升或降低優先順序(由作業系統完成); 

3. 長時間得不到執行而被提公升優先順序。

在多個執行緒併發執行訪問同乙個資料時,如果不採取相應的措施,將會是非常危險的。假設你在工行有乙個銀行賬戶,兩張銀聯卡(自己手裡一張,女朋友手裡一張),裡面有100萬。假設取錢就兩個過程:1.檢查賬戶餘額,2.取出現金(如果要取出的金額 > 賬戶餘額,則取現成功,否則取現失敗)。有一天你要買房想把錢取出來,而此時你女朋友也想買一輛車(假設你們事先沒有商量)。兩個人都在取錢,你在a號atm機取100萬,女朋友在b號atm機取80萬。這時a號atm檢查賬戶餘額發現有100萬,可以取出;而與此同時,同一時刻b號atm也在檢查賬戶餘額發現有100萬,可以取出;這樣,a、b都把錢取出來了。

100萬的存款取出180萬,銀行就虧大發了(當然你就笑呵呵了……)!這就是執行緒併發的不安全性。為避免這種情況發生,我們要將多個執行緒對同一資料的訪問同步,確保執行緒安全。

所謂同步(synchronization)就是指乙個執行緒訪問資料時,其它執行緒不得對同乙個資料進行訪問,即同一時刻只能有乙個執行緒訪問該資料,當這一線程訪問結束時其它執行緒才能對這它進行訪問。同步最常見的方式就是使用鎖(lock),也稱為執行緒鎖。鎖是一種非強制機制,每乙個執行緒在訪問資料或資源之前,首先試圖獲取(acquire)鎖,並在訪問結束之後釋放(release)鎖。在鎖被占用時試圖獲取鎖,執行緒會進入等待狀態,直到鎖被釋放再次變為可用。

二元訊號量(binary semaphore)是一種最簡單的鎖,它有兩種狀態:占用和非占用。它適合只能被唯一乙個執行緒獨佔訪問的資源。當二元訊號量處於非占用狀態時,第乙個試圖獲取該二元訊號量鎖的執行緒會獲得該鎖,並將二元訊號量鎖置為占用狀態,之後其它試圖獲取該二元訊號量的執行緒會進入等待狀態,直到該鎖被釋放。

多元訊號量允許多個執行緒訪問同乙個資源,多元訊號量簡稱訊號量(semaphore),對於允許多個執行緒併發訪問的資源,這是乙個很好的選擇。乙個初始值為n的訊號量允許n個執行緒併發訪問。執行緒訪問資源時首先獲取訊號量鎖,進行如下操作: 

1. 將訊號量的值減1; 

2. 如果訊號量的值小於0,則進入等待狀態,否則繼續執行; 

訪問資源結束之後,執行緒釋放訊號量鎖,進行如下操作: 

1. 將訊號量的值加1; 

2. 如果訊號量的值小於1(等於0),喚醒乙個等待中的執行緒;

互斥量(mutex)和二元訊號量類似,資源僅允許乙個執行緒訪問。與二元訊號量不同的是,訊號量在整個系統中可以被任意執行緒獲取和釋放,也就是說,同乙個訊號量可以由乙個執行緒獲取而由另一線程釋放。而互斥量則要求哪個執行緒獲取了該互斥量鎖就由哪個執行緒釋放,其它執行緒越俎代庖釋放互斥量是無效的。

臨界區(critical section)是一種比互斥量更加嚴格的同步手段。互斥量和訊號量在系統的任何程序都是可見的,也就是說乙個程序建立了乙個互斥量或訊號量,另一程序試圖獲取該鎖是合法的。而臨界區的作用範圍僅限於本程序,其它的程序無法獲取該鎖。除此之處,臨界區與互斥量的性質相同。

讀寫鎖(read-write lock)允許多個執行緒同時對同乙個資料進行讀操作,而只允許乙個執行緒進行寫操作。這是因為讀操作不會改變資料的內容,是安全的;而寫操作會改變資料的內容,是不安全的。對同乙個讀寫鎖,有兩種獲取方式:共享的(shared)和獨佔的(exclusive)。當鎖處於自由狀態時,試圖以任何一種方式獲取鎖都能成功,並將鎖置為對應的狀態;如果鎖處於共享狀態,其它執行緒以共享方式獲取該鎖,仍然能成功,此時該鎖分配給了多個執行緒;如果其它執行緒試圖如獨佔的方式獲取處於共享狀態的鎖,它必須等待所有執行緒釋放該鎖;處於獨佔狀態的鎖阻止任何執行緒獲取該鎖,不論它們以何種方式。獲取讀寫鎖的方式總結如下:

讀寫鎖的狀態

以共享方式獲取

以獨佔方式獲取

自由成功

成功共享

成功等待

獨佔等待等待

表 1 :獲取讀寫鎖的方式

多執行緒 二 執行緒優先順序 守護執行緒

多執行緒 二 執行緒優先順序 守護執行緒 多執行緒 三 執行緒訪問變數 多執行緒 四 執行緒異常抓捕 多執行緒 五 執行緒池 多執行緒 六 鎖與同步 多執行緒 七 阻塞佇列 多執行緒 八 非同步計算結果獲取 多執行緒 九 android非同步任務 方式 說明thread setpriority th...

多執行緒多程序優先順序理解 優先順序反轉

1.優先順序反轉 priority inversion 由於多程序共享資源,具有最高優先權的程序被低優先順序程序阻塞,反而使具有中優先順序的程序先於高優先順序的程序執行,導致系統的崩潰。這就是所謂的優先順序反轉 priority inversion 2.產生原因 其實,優先順序反轉是在高優級 假設為...

多執行緒 執行緒優先順序

目錄 1 執行緒優先順序具有繼承性 2 執行緒優先順序的作用 thread類原始碼中定義 最低優先順序為1 public final static int min priority 1 一般沒有繼承,沒有顯示指定優先順序時,預設優先順序為5 public final static int norm ...