一文看懂臨界區、互斥鎖、同步鎖、臨界區、訊號量、自旋鎖等名詞!

業餘草發表於2019-02-20

點選上方“業餘草”,選擇“置頂公眾號”

第一時間獲取技術乾貨和業界資訊!

 

640?wx_fmt=png

 

關於執行緒安全的專有名詞有一大堆。你們突然之間問我這個名詞是什麼意思,那個名詞是什麼意思我還真不一定能給你準確的回答。這還別說一門語言一堆名詞。其實有些名詞叫法不同,實際上就是一個意思。

A 語言有這個名詞,B 語言就起另外一個名詞。不能大膽的雷同,所以就改變一個叫法,其本質還是一樣的。

為了減少大家在私信我,那我今天就來扯一扯,競態條件,競態資源,輪詢忙等,鎖變數,原子性,TSL,阻塞,睡眠,喚醒,管程,互斥鎖,同步鎖,臨界區,互斥量,訊號量,自旋鎖等各個專業名詞的實際所代表的含義。

競態條件:多執行緒的核心矛盾是“競態條件”,即多個執行緒同時讀寫某個欄位。

競態資源:競態條件下多執行緒爭搶的是“競態資源”。

臨界區:涉及讀寫竟態資源的程式碼片段叫“臨界區”。

互斥:保證竟態資源安全的最樸素的一個思路就是讓臨界區程式碼“互斥”,即同一時刻最多隻能有一個執行緒進入臨界區。

最樸素的互斥手段:在進入臨界區之前,用if檢查一個bool值,條件不滿足就“忙等”。這叫“鎖變數”。但鎖變數不是執行緒安全的。因為“檢查-佔鎖”這個動作不具備“原子性”。

TSL:“TSL指令”就是原子性地完成“檢查-佔鎖”的動作。

自旋鎖:就算不用TSL指令,也可以設計出執行緒安全的程式碼,有一種既巧妙又簡潔的結構叫“自旋鎖”。當然還有其他更復雜的鎖比如“Peterson鎖”。

忙等待:但自旋鎖的缺點是條件不滿足時會“忙等待”,需要後臺排程器重新分配時間片,效率低。

解決忙等待問題的是:“sleep”和“wakeup”兩個原語。sleep阻塞當前執行緒的同時不會讓出它佔用的鎖。wakeup可以喚醒在目標鎖上睡眠的執行緒。

互斥量:使用sleep和wakeup原語,保證同一時刻只有一個執行緒進入臨界區程式碼片段的鎖叫“互斥量”。

訊號量:把互斥鎖推廣到"N"的空間,同時允許有N個執行緒進入臨界區的鎖叫“訊號量”。互斥量和訊號量的實現都依賴TSL指令保證“檢查-佔鎖”動作的原子性。

管程:把互斥量交給程式設計師使用太危險,有些程式語言實現了“管程”的特性,從編譯器的層面保證了臨界區的互斥,比如Java的synchronized關鍵字。

互斥鎖、獨佔鎖、內建鎖:並沒有“同步鎖”這個名詞,Java的synchronized正確的叫法應該是“互斥鎖”,“獨佔鎖”或者“內建鎖”。但有的人“顧名思義”叫它同步鎖。

下面我們簡單的來擴充套件一下。比如下面的程式:

640?wx_fmt=png

多執行緒同時執行這段程式碼可能就會出錯。當兩個執行緒競爭同一資源時,如果對資源的訪問順序敏感,就稱存在競態條件。導致競態條件發生的程式碼區稱作臨界區。上例中 add() 方法就是一個臨界區,它會產生競態條件。在臨界區中使用適當的同步就可以避免競態條件。

640?wx_fmt=png

上面程式碼中 occupied 就是鎖變數。

自旋鎖的關鍵就是用一個 while 輪詢,代替 if 檢查狀態,這樣就算執行緒切出去,另一個執行緒也因為條件不滿足迴圈忙等,不會進入臨界區。這是一個非常常用的結構,不光用在自旋鎖,基本是使用條件變數 wait(),notifyAll() 時候的一種慣用法。

640?wx_fmt=png

自旋鎖的缺點是迴圈忙等。如果併發的執行緒不像程式排程那樣在時間片用完以後會自動切換上下文,就會形成死鎖。所以最好在條件不滿足的時候,讓出執行緒的控制權,讓其他執行緒有機會執行來使條件滿足。

執行緒安全不管搞出多少名詞,都逃不出 3 大核心:原子性、可見性、有序性。推薦閱讀《Java 執行緒安全的3大核心:原子性、可見性、有序性》,其他的相關鎖知識,請檢視我公眾號裡的相關文章。

原文連結:https://www.xttblog.com/?p=3865

640?wx_fmt=png

10T技術資源大放送!包括但不限於:C/C++,Linux,Python,Java,PHP,人工智慧,GO等等。在公眾號內回覆對應關鍵字或框架名字,即可免費獲取!!

 你再主動一點點 640?  我們就有故事了

相關文章