為什麼多線程是個壞主意
在 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 和 分布式係統)
-
隻有當性能要求很高的服務時候,才使用 多線程
最後更新:2017-05-19 14:32:18