為什麼多執行緒是個壞主意
阿新 • • 發佈:2018-12-23
原文地址:
在 Unix程式設計藝術 中,提到了儘量避免多執行緒程式設計模型, 認為這樣只會增加複雜度, 提倡使用多程序, 這樣本質上就可以避免多執行緒『共享記憶體資料』產生的 “corruotped memory” 問題。
其中, 提到了一篇文章 Why Threads Are A Bad Idea, 對於多執行緒程式設計和事件程式設計分析的非常好, 具體的翻譯如下:
1 介紹
執行緒的背景:
- 在作業系統中出現多執行緒
- 逐漸演變成 使用者層面的程式設計工具
- 被認為是多種問題的一種通用解決方案
- 每一個程式設計師都需要成為 一個多執行緒程式設計的高手嗎?
根本性的問題:
多執行緒的程式非常難以正確的編寫!!!
替代性的方案:
使用事件驅動的程式設計方法
特別宣告:
- 對於大部分的多執行緒程式,使用事件驅動是一個更好的選擇
- 只有當使用CPU多核的時候, 才需要使用多執行緒程式設計
2 多執行緒的本質
- 一般用來管理併發問題
- 多個獨立相互執行的任務
- 共享的記憶體
- 預先的安排機制(Pre-emptive scheduling)
- 同步機制(synchronization)
3 多執行緒的用途
- 作業系統: 對每一個使用者程序分配一個核心執行緒
- 科學應用程式: 每個CPU分配一個執行緒(對計算要求性很高的程式)
- 分散式系統: 程序請求並行(同步記性的I/O操作)
- GUIs程式
- 執行緒對應使用者的行為. 在長時間的後臺計算過程中仍然可以處理圖形展示
- 多媒體, 動畫方面的程式編寫
4 多執行緒有什麼問題?
- 對於一般的程式設計師而言,難以掌握。
- 即使對於專家,多執行緒程式設計也是痛苦的。
5 為什麼多執行緒程式設計很難?
- Synchronization(同步機制):
- 必須通過鎖來共享資料
- 忘記了加鎖?就會導致受汙染的資料
- 死鎖
- 依賴鎖,會導致迴圈依賴
- 每個處理程式等待其他處理程式: 導致系統掛起
6 為什麼多執行緒程式設計很難?
- 難以除錯: 因為 資料依賴,時間依賴
- 執行緒破壞了抽象: 無法設計出模組化的程式
- 因為鎖導致回撥無法完成
7 為什麼多執行緒程式設計很難?
- 很難達到非常好的效能
- 簡單的鎖導致了低併發
- 而精密的鎖又會導致複雜度提升, 降低了一般情況下的效能
- OSes限制了效能提升(排程, 環境切換)
- 執行緒不受支援
- 難以支援多執行緒程式碼(mac, windows)
- 一些標誌庫不是執行緒安全的
- 核心呼叫, windows系統不是多執行緒
- 很少有多執行緒程式設計的除錯工具
- 通常不需要併發場景
8 時間驅動程式設計
- 一個執行流程序: 沒有CPU的併發
- 在時間上註冊訊息(通過回撥)
- 事件輪詢等待訊息, 呼叫處理器模型
- 時間處理器沒有搶斷
- 處理器通常是 短生命週期的
9 事件驅動程式設計被用來幹什麼
- 大多數的GUIs程式設計:
- 一個處理器對應一個事件
- 處理器用來執行行為(撤銷,刪除檔案等)
- 分散式系統
- 一個處理器用來對應一個輸入源
- 處理進來的請求,返回結果
- 事件驅動的I/O 來處理 I/O併發
10 事件驅動程式設計的問題
- 長時間執行的時間處理器會導致 程式沒有反應, 解決辦法:
- 對於長時間執行的程式Fork off子程式處理, 當處理結束後使用事件
- 打斷處理器執行(比如: 事件驅動的I/O)
- 定期回撥 時間處理器中的 事件迴圈
- 通過處理器無法維護本地記憶體狀態(處理器必須返回)
- 沒有CPU的併發(不太合適科學計算程式)
- 事件驅動的程式設計並不總是被支援
11 多執行緒程式設計 VS 事件驅動程式設計
-
事件驅動編髮程式設計儘可能的避免 併發, 而多執行緒程式設計則傾向於併發:
- 使用事件驅動程式設計更加容易: 不用考慮併發, 不用考慮搶佔, 不用考慮同步和死鎖
- 只在特定的情況下,才使用複雜的技術棧
- 使用多執行緒程式設計, 即使最簡單的程式也需要面對很高的複雜度(full complexity)
-
使用事件驅動更加容易除錯
- 事件驅動程式設計只和時間依賴有關, 不需要考慮內部的排程
- 問題更加容易跟蹤: 較慢的按鈕點選反應 和 記憶體資料汙染 時候, 前者問題更加容易定位
12 多執行緒程式設計 VS 事件驅動程式設計
-
在單個CPU上時間驅動程式比執行緒更加快速
- 沒有鎖的覆蓋
- 沒有上下文環境的 切換
-
事件驅動程式設計更加面向介面程式設計
-
多執行緒提供了真正的併發性
- 對於多CPU的機器來說,是可以擴充套件效能
- 可以長時間的執行處理程式而不需要凍結
13 你需要放棄多執行緒嗎?
-
不需要的情況: 對於應該程式效能要求很高的服務(比如: 資料庫伺服器)
-
但是, 儘可能的避免多執行緒程式設計:
- 對於 GUIs程式, 分散式系統, 效能要求不高的, 使用事件程式設計, 不是多執行緒
- 只有當真正的多核CPU併發需要使用到的時候,使用多執行緒程式設計
- 當使用多執行緒程式設計的時候,將多執行緒程式設計模組與其他模組進行隔離, 保持大部分程式碼都是單執行緒模型
14 總結
-
併發從根本上是很難的, 儘可能的避免
-
多執行緒比事件更加強大,但是這種強大的功能很少真正需要
-
多執行緒程式設計比事件程式設計更加難以寫出正確的程式碼, 只有真正的專家才能掌握
-
將事件 程式設計當做基本的開發工具(對於GUIs 和 分散式系統)
-
只有當效能要求很高的服務時候,才使用 多執行緒