併發思想提煉(1)(理解併發,避免死鎖)

併發思想提煉(1)(理解併發,避免死鎖)html

一直作服務器後端和基礎組件平臺開發,經常用到併發,故簡單放些乾貨,一來算是總結,二來但願後人少走彎路, 寫到哪兒算哪兒,不按期更新。python

1.    Introduction編程

先來明白一些概念。Concurrency併發和Multi-thread多線程不一樣後端

你在吃飯的時候,忽然來了電話。安全

  1. 你吃完飯再打電話,這既不併發也很少線程
  2. 你吃一口飯,再打電話說一句話,而後再吃飯,再說一句話,這是併發,但很少線程。
  3. 你有2個嘴巴。一個嘴巴吃飯,一個嘴巴打電話。這就是多線程,也是併發。

併發:表示多個任務同時執行。可是有可能在內核是串行執行的。任務被分紅了多個時間片,不斷切換上下文執行。服務器

多線程:表示確實有多個處理內核,可同時處理多個任務。多線程

世界的複雜的,世界是併發的。因而模擬這世上的業務的程序也是併發的。隨着系統功能的增長,複雜度不斷提升,併發特性被引入編程。架構

2.    最簡單的併發併發

兩個任務互不干擾,它們不會影響到系統的同一實體。每一個線程只須要本身作好本身的任務便可。線程

兩個任務會影響到同一實體,可是不會在同時訪問該同一實體。這樣,在這個實體上,任務是串行執行的。這樣也是安全的併發。

3.    危險的併發

兩個任務同時訪問同一實體,髒讀寫的問題,設有a值爲1, 兩個線程前後加1,按道理說最後a值結果爲3.

  1. T1線程讀取數據a;T2線程讀取數據a;
  2. T1線程a++, 而後反存到a,a值爲2;
  3. T2線程a++, 而後反存到a, a值仍是爲2;

兩次操做後,a值不爲3,而是2。這就是併發出現的錯誤。a如果一個可釋放(disposable)的實體. T1線程釋放a,T2線程操做a,會形成更大的錯誤。

4.    如何避免此危險

1. 乾脆就串行執行T1,T2。不過沒有利用處處理器的併發特性。雖然安全,可是效率不高。

2. T1,T2併發。可是不會同時操做同一個實體(Critical Entity)。即實體不是併發的。實體是串行的。

3. 讀取關鍵實體使用Clone,拷貝出來的實體是臨時的。本次操做在該臨時實體上。下次操做繼續Clone該實體再使用。

常常能用到的是方法2和方法3。接下來具體說說方法2和3。

5.    讓對關鍵實體的訪問串行

方法2的核心思想是串行。不過不是任務串行,而是訪問共享實體時串行。串行是人類容易思惟的方式,把併發問題轉變爲順序執行問題,也助於後來維護人員理解。一個最簡單的實現方式就是加Lock then access。

 

6.    Lock地獄

Lock是併發程序中經常使用的操做,每一個人都會用。。。。嗯。。。。經常會濫用。。。而後,運行一段時間。嘣,程序自爆。說說經常遇到的問題:

  1. 死鎖。T1 lock A request B,T2 lock B request A。T1和T2被互相Block住。程序進行不了
  2. 遞歸鎖。T1 lock A and lock A again。同一線程T1被本身Block住。

遞歸鎖好解決,C++ 11中有std::recursive_mutex。再高級一點的語言自帶這樣的特性。好比C#中的lock就自帶遞歸鎖。通常地,遞歸鎖經過裏面添加counter實現,每次鎖就counter++,每次release就counter--。Counter 爲0就表示解鎖。其實這就是一種信號量(semaphore)的實現方法。引伸開來去,C++中的share_ptr/auto_ptr就是此類思想,這種經過引用計數來判斷該對象是否被使用,若檢測到不被使用從而自動的release該段內存。C#和Java中內存管理就是這個思路。很少講了,之後單開段子講。

7.    死鎖及其防止

確切的說死鎖單純在程序這個層面難以防止,最好在設計開始就注意這個問題。就是說在程序設計的時候就搞明白那些資源會互相調用。這樣的狀況就要多留心眼。我工做中一直寫一些基礎架構API。當其它工程師調用時。。。。。嗯。。。。。不仔細看文檔,在一些不適用的地方使用形成了死鎖,而後來找我。。。。。這種狀況下要麼就多培訓,多強調使用手冊。要麼,這樣說吧,把API寫得健壯點。畢竟徹底不會知道用戶會怎麼使用。這就是我在API開發中使用靜態語言的緣由(其實我更喜歡動態語言python,用python作leetcode真是心情舒暢),至少在編譯階段就能有必定程度的規則控制,而不是到了運行階段報Error。這關係到如何編寫健壯的API,之後開段子講。

回到死鎖防止這個問題,關於避免死鎖的API可使用這個方法---try lock機制。簡單就是說給lock一個時間鎖,若是在一段時間內仍是沒有取得該資源的訪問權就跳過,放LOG,而後執行下面的步驟。能夠經過前面講的「等待信號量」來實現此機制,其實也不用本身特別實現,各主流語言應該都有。核心思想在軟件層面上是放個自旋鎖和Flag量,以爲搞不明白(C++,C#,Java等該功能實現方式)的話本身也能夠實現一個。

 

第2篇 Lock free, 輪詢和線程池

相關文章
相關標籤/搜索