1. 程式人生 > >多執行緒與加鎖的一些理解

多執行緒與加鎖的一些理解

  • 競爭發生在當多執行緒同時訪問同一個記憶體,各個執行緒執行的順序有交叉時。
  • 一般來說,區域性變數和引數由於對每個執行緒是獨立的,不會發生競爭。競爭通常發生在全域性變數,堆變數等上。
  • 解決競爭最簡單的方法就是進入臨界區(開始讀寫公共記憶體)時遮蔽掉中斷,但是這種方法對於多CPU無效,因為該記憶體可能會被別的CPU中的程式訪問。現在更好的方法是遮蔽那片記憶體的訪問(以前是通過鎖住匯流排解決的)。但是還是會有問題,因為這樣也遮蔽了時鐘中斷,可能會陷入死鎖。
  • 第二種想法就是加鎖,但是簡單的在程式級別進行加鎖是沒用的,因為加鎖需要先測試相等,然後增加或減少鎖變數兩個步驟,這之間可能也會存在競爭。因此現在是在硬體層面實現了原子操作TSL(測試並加鎖),這個操作在完成前無法被中斷,本質上和下面的自旋鎖一樣。
  • 加鎖的方法有很多,一種叫Peterson的演算法可以看我上一篇部落格。現在介紹最簡單的自旋鎖。
while(true != 0)
;
critical_region();
turn = 1;
nonciritical_region();

可以看出自旋鎖就是在測試變數不處理的時候進行忙等待。這種方法是假設忙等待的時間很短,一般在核心中使用。和別的方法比優點在於不用切換程序。

  • 最常用的手段就是使用訊號量或者更簡單一點的互斥量,這裡比較簡單,不在贅述。
  • 和互斥量經常一起使用的是條件變數,即如果不滿足條件就睡眠(釋放CPU給其他執行緒)直到滿足條件。他要和互斥量一起使用(互斥量通常作為引數)的原因在於互斥量要保護wait過程不被中斷,具體見下面的圖片。
    在這裡插入圖片描述
    另外,條件變數不想訊號量會儲存在記憶體中,如果傳遞時沒有執行緒在等待條件變數的操作(如喚醒),那麼這個訊號就會丟失。
    在這裡插入圖片描述

在這裡插入圖片描述

  • 由於訊號量的操作容易出錯,在更高階的語言裡(如JAVA),產生個管程的概念,和前面的本質是一樣的,只是這裡加鎖和解鎖由編譯器實現,減少了出錯的可能性。