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

来源:http://www.cnblogs.com/ganmuren/archive/2016/03/15/5279201.html
-Advertisement-
Play Games

併發思想提煉(1)(理解併發,避免死鎖) 一直做伺服器後端和基礎組件平臺開發,常常用到併發,故簡單放些乾貨,一來算是總結,二來希望後人少走彎路, 寫到哪兒算哪兒,不定期更新。 1.    Introduction 先來明白一些概念。Concurrency併發和Multi-thread多線程不同 你在


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

一直做伺服器後端和基礎組件平臺開發,常常用到併發,故簡單放些乾貨,一來算是總結,二來希望後人少走彎路, 寫到哪兒算哪兒,不定期更新。

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等該功能實現方式)的話自己也可以實現一個。

 (to be continue....)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Spring能夠在classpath下自動掃描,偵測和實例化具有特定註解的組件,這在Spring中成為組件掃描(Component scanning). @Component:基本註解,標識了一個受spring管理的組件. @Repository:標識持久層組件 @Service:標識服務層(業務層
  • 錯誤 C4996 初學C語言時,第一個接觸到的I/O函數便是scanf()了。但在高版本的 Visual Studio (包括但不限於2015、2013、2012)編譯代碼時,卻會出現意想不到的錯誤。有如下一段簡單的代碼: #include "stdio.h" int main(void) { in...
  • 繼承 概念: ① 繼承背後的思想就是基於已存在的類來構建新類; ② 當從已存在類繼承時,就重用了它的方法和屬性,還可以添加新的方法和屬性來定製新類以應對需求; ③ 當從其它類導出的類叫作子類,被導出的類叫作父類; ④ 在Java中,除了Object類外,所有類都是子類,都有唯一父類; ⑤ 繼承在OO
  • 對象A的創建過程: 1. 構造器實際上是靜態方法,所以,當首次創建對象A 或者 A類的靜態方法/靜態域首次被訪問時,Java解釋器查找類路徑,以位 A.class文件。 2. 載入A.class,有關靜態初始化的所有動作都會執行()。因此,靜態初始化只在Class對象首次載入的時候進行一次。 3. ...
  • 本篇分為兩部分:   Playground 就是提供一個可以即時編譯的類似 REPL 的環境,他為我們提供了一個順序執行的環境,在每次更改其中代碼後整個文件都會被重新編譯,並清空原來的狀態並運行。 NSTimer 在預設的 Playground 中是不會執行的: 在執行玩 NSTimer 語句之後,
  • 想了很久不知道怎麼入手 沒思路 不會寫    後來直接說不會寫   又給我出了一個 2+(3*2)+2 實現計算器輸入 輸出結果 讓我手寫出程式  蒙了 一塊面試的幾個人都沒做出面試題來 很是受打擊啊 醉了
  • 這是一本框架性的書,它從分散式網站架構設計需要考慮的內容出發,介紹了相關的技術。雖然每一部分講解不是特別深入,但是原理清晰,案例豐富,非常不錯。 現在的網站都越做越大,分散式是必須的選擇,通過這本書我看到了淘寶是怎麼做的,對於今後的工作很有幫助。 作者也是一位非常年輕的工程師,很不錯
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...