併發程式設計之臨界區\阻塞\非阻塞\死鎖\飢餓\活鎖

字母哥部落格發表於2022-05-11

本文介紹併發程式設計中的若干概念,實際上在筆者之前的文章中,已經介紹過很多概念。比如:併發與並行、同步與非同步、鎖與訊號量等等。參考《併發程式設計專欄》,本文計息介紹一些相對深入一些的概念

一、臨界區

為了方便大家理解,我們先看下面的這樣一張圖,我們可以把房子看作一個程式,每個房子裡面的住戶及其活動看作一個執行緒,飲水機、健身器材、廁所都屬於共享資源。這裡的共享資源實際就是臨界區的概念,臨界區的資源在同一時間只能被一個執行緒(住戶)使用,所以一旦臨界資源被佔用,其他的執行緒(住戶)能做的就只有等待。

比如,在一個出租房內,住戶A佔用了廁所,在他使用廁所的這段時間內廁所這個資源就是他獨佔的。如果住戶B此時也想上廁所,就只能等待住戶A上完廁所之後才可以繼續使用該資源。

二、阻塞和非阻塞

瞭解了臨界區的概念之後,阻塞概念就好理解了。一個執行緒先佔用了臨界區的資源,此時如果其他的執行緒想使用臨界區資源就必須等待。這種佔用臨界區資源,阻塞其他執行緒繼續執行的情況就是執行緒阻塞(Blocking)。

然而說到非阻塞,一般說的就是是否對當前執行緒自己產生阻塞,比如:

  • 我執行一個任務,比如使用飲水機接水。我拿了一個杯子接水,而我必須在飲水機前面等著水接完,這種就是阻塞式執行緒。
  • 如果我拿了杯子接水,把杯子放到飲水機下面,飲水機會在杯子接滿水之後,自動對我發出非同步通知(比如聲音告警)。我可以在此期間做其他的事情,這種就是非阻塞式執行緒(Non-Blocking)。非阻塞式執行緒,從程式設計的角度一般都是通過回撥函式,或者響應式程式設計(Reactive programming)實現的。

三、死鎖、飢餓和活鎖

  • 死鎖:我們來看上面的這張圖,在十字路口A車道的A1車像轉向B車道,但B車道入口被B1車佔用;在十字路口B車道的B1車像轉向C車道,但C車道入口被C1車佔用;在十字路口C車道的C1車像轉向D車道,但D車道入口被D1車佔用;在十字路口D車道的D1車像轉向A車道,但A車道入口被A1車佔用;也就是說:執行緒因為資源競爭,彼此需要資源又都無法釋放,導致執行緒無法獲取下一步執行所需的資源,導致死鎖產生。

  • 飢餓:舉個例子農民給小雞餵食,如果有五隻雞每次都剛好給五隻雞的飼料量。無法避免的是有的雞吃的量超過規劃量,最總導致某一隻雞無法吃到足夠的飼料。由於它無法遲到足夠的飼料,它就比較瘦弱,搶食的能力弱。惡性迴圈,最終很可能被餓死。具體到程式設計層面,就是某一執行緒A的優先順序比較低,然後優先順序高的執行緒又經常佔用資源不釋放,執行緒A長期無法得到有效執行,處於飢餓狀態。極端情況下,可能被餓死。

  • 活鎖:相對於死鎖和飢餓,活鎖是一種相對好的狀態。大家在生活中肯定遇到過這樣一種情況,你在樓梯拐角遇到一個同事,空間有限所以二人茬住了。你向左移動,你的同事也向左移動;你向右移動,你的同事也向右移動;所以你們兩個人都無法向前移動,這就是一個典型的活鎖。因為人是高智慧的動物,又都懂得禮讓,所以對於人來說活鎖的問題很好解決,只要其中一個人原地不動,另一個人動一下就過去了。但是多執行緒面對活鎖的時候就沒有那麼智慧了,有可能出現不斷地釋放資源(向左移)、佔用資源(向右移)的迴圈中,即使最終活鎖被解開,其資源開銷及時間成本都是很大的。

歡迎關注我的部落格,更多精品知識合集

本文轉載註明出處(必須帶連線,不能只轉文字):字母哥部落格 - zimug.com

覺得對您有幫助的話,幫我點贊、分享!您的支援是我不竭的創作動力!。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

相關文章