1. 程式人生 > >如何寫出執行緒安全的類和函式

如何寫出執行緒安全的類和函式

       什麼是執行緒安全的類和函式,可以被多個執行緒呼叫而不會出現資料的錯亂的類和函式被叫做執行緒安全的類和函式,首先導致執行緒不安全的根本原因是我們函式中或著類中的共享成員變數(如類靜態成員變數,全域性變數),當我們的函式中或者類中有這些變數時他們都是非執行緒安全的,當有多個執行緒呼叫這些函式或者物件時,就會由於沒有對這些變數進行同步操作而產生資料錯亂。那麼什麼樣的函式和類可以被多個執行緒呼叫而不會出現資料錯亂呢。

 一  無狀態的

       這類函式和類不包含任何其他其他作用域中的變數活著物件,計算過程中的臨時狀態僅存在於自己的執行緒棧上的區域性變數中,並且只能有當前的執行緒訪問,不會受到其他執行緒的影響,這些無狀態的類和函式時執行緒安全的。

      非執行緒安全例子:

       1: 函式中引用了全域性可變變數或者物件。並且函式的執行狀態依賴該變數當前狀態的影響,此時每次的執行結果是不確定的,受到其他執行緒的影響,是非執行緒安全的。

       2: 函式中引用或者返回了全域性靜態變數,區域性靜態變數,類靜態變數,因為靜態變數只會初始化一次,他的狀態會受到其他執行緒的影響。

       3: 如果一個類中含有可變的靜態變數,那麼此類也是非執行緒安全的,當我們在不同的執行緒中定義了該類的物件時(其實不用定義該類的物件也可),如果線上程中改變了該類的靜態成員變數,那麼也會造成資料的不一致。

      4:呼叫了執行緒不安全的函式,那麼該函式揮著類也是非執行緒安全的。

 二  可重入函式

        可重入函式不會應用任何共享資料,並且函式引數也是值傳遞而非指標或者引用傳遞,他不需要任何的額外的同步操作就能保證它的執行緒安全性。這類函式成為顯式可重入函式,如果我們的指標或者引用引數沒有引用共享變數或者全域性變數也是可重入的,叫做隱式可重入函式。

       一般下面的函式都是不可重入的,也不非執行緒安全的。

      1: 函式內使用了靜態的資料結構(這個和上面所說的基本相同)

       2: 函式體內使用了malloc和freee函式,(使用了全域性記憶體分配表)

      3: 使用標準io函式(使用一些全域性的系通資源,printf使用了全域性的stdout)

三 執行緒同步

       上面說的都是沒有任何同步機制情況下的執行緒安全函式,如果我們通過一些同步手段,以上的函式或者類會變成執行緒安全的函式。如果沒有同步措施,通常在單執行緒中如果我們向某個變數寫入值然後在讀值我們總能得到正確的結果或者說唯一的確定的一個結果,但是當這些操作在不同的執行緒中執行時情況就會變的非常複雜,我們無法保證正在執行讀操作的執行緒能正確的讀到其他執行緒所做的改變,並且在沒有同步機制的環境下,編譯器或者處理器會對我們程式的執行順序進行一些調整,這些調整在單執行緒環境下不會產生影響,但是在多執行緒環境中會造成一些意想不到的結果,如果我們的執行緒之間相互影響,或者我們想讓某些執行緒按照我們的安排來執行,此時如果讓他們完全由處理器去控制,就會失去控制,此時就算沒有執行緒之間沒有共享資料也會產生錯誤,因此我們需要一些額外的手段來保證執行緒的正確執行。

      通常我們通過加鎖來實現資料的同步和保護,加鎖的目的就是保證同一時刻只有一個執行緒在操作我們所要保戶的程式碼區的共享資料,達到資料的一致性和同步性,但是我們不能盲目的加鎖,碰到資料就加鎖,鎖是需要消耗資源的,如果我們盲目的加鎖會造成不必要的資源浪費。因此我們要仔細分析程式中的資料共享性,僅對那些共享的資料程式碼塊加鎖即可,但是有時候加鎖也並不達到效果,當我們的鎖在一個類中時,我們要對類的某個例項進行保護時,通常我們在成員函式中加鎖,如果我們不小心把成員變數通過成員函式的返回值或者通過成員函式一個引用或者指標引數,把成員變數傳遞到了鎖的保護範圍之外,此時成員變數的正確性已經沒有保證了,因此對於鎖的使用要很小心。

      如果我們想實現執行緒之間的同步,而不是讓處理器來控制執行緒或者程序的執行,我們可以用訊號量pv操作來實現,它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目 .訊號量物件對執行緒的同步方式與前面幾種方法不同,訊號允許多個執行緒同時使用共享資源,這與作業系統中的PV操作相同。它指出了同時訪問共享資源的執行緒最大數目。它允許多個執行緒在同一時刻訪問同一資源,但是需要限制在同一時刻訪問此資源的最大執行緒數目。

下面還有兩種也會用到的方式

       互斥量:採用互斥物件機制。 只有擁有互斥物件的執行緒才有訪問公共資源的許可權,因為互斥物件只有一個,所以能保證公共資源不會同時被多個執行緒訪問。互斥不僅能實現同一應用程式的公共資源 安全共享,還能實現不同應用程式的公共資源安全共享 .互斥量比臨界區複雜。因為使用互斥不僅僅能夠在同一應用程式不同執行緒中實現資源的安全共享,而且可以在不同應用程式的執行緒之間實現對資源的安全共享。
       事 件: 通過通知操作的方式來保持執行緒的同步,也可以方便實現對多個執行緒的優先順序比較的操作 .