並發基礎練習題 (Practice - Concurrency Fundamentals)
Related Concepts
- 01-Concurrency-Fundamentals/01-What-Is-Concurrency
- 01-Concurrency-Fundamentals/02-Why-Use-Concurrency
- 01-Concurrency-Fundamentals/03-Cpp-Concurrency-Support-and-First-Program
| 關鍵字 | 答案 |
|---|---|
| 單核「同時」跑多程式 | 任務切換(並發的假象) |
| 真正平行 | 硬體並發(多核/多處理器) |
| 行程 vs 執行緒 | 隔離+IPC 慢 vs 共享位址空間+開銷小 |
| 平行 vs 並發 | 效能 vs 任務分離/回應性 |
| 使用並發的理由 | SoC 與 效能 |
| 拆單一任務 vs 拆資料 | 任務平行 vs 資料平行 |
| 不用並發的理由 | 收益比不上成本 |
| C++11 帶來 | 記憶體模型 + 標準執行緒庫 |
| 高階 vs 底層開銷差 | 抽象懲罰 (abstraction penalty) |
| 平台特有功能 | native_handle() |
Question 1 - 並發的定義 [recall]
請說明「並發」的一般定義,以及它在電腦系統中的意義。
並發:兩個或兩個以上的獨立活動同時發生。在電腦系統中,指單一系統同時執行多個獨立任務。並發不是新事物(多工 OS 早已存在),近年流行的原因是多核處理器帶來真正的平行。
Question 2 - 任務切換 vs 硬體並發 [recall]
單核機器只有一個處理核心,為什麼仍能「同時」執行多個應用程式?這與多核機器的並發有何本質差異?
單核靠任務切換:單位時間內對任務多次快速輪替,造成「並發的假象」。多核機器具硬體並發,可真正平行執行多個任務。注意:即使是多核系統,任務數通常仍超過硬體執行緒數,因此任務切換依然存在;兩者行為上有微妙差異,不能完全等同。
Question 3 - Context switch 成本 [recall]
進行一次任務切換 (context switch) 時,作業系統必須做哪些工作?為什麼會造成延遲?
OS 必須:(1) 保存當前任務的 CPU 狀態與指令指標;(2) 計算要切換到哪個任務;(3) 將新任務載入處理器。此外 CPU 可能要把新任務的指令與資料載入快取,期間 CPU 停止執行指令,造成額外延遲。
Question 4 - 多行程 vs 多執行緒 [recall]
比較多行程並發與多執行緒並發在通訊方式、開銷與安全性上的差異;C++ 標準對哪一種提供原生支援?
多行程:透過 IPC(訊號、socket、檔案、管線)通訊——複雜且慢(OS 保護行程資料),啟動與管理開銷高;優點是 OS 提供保護、較易寫出安全的並發程式,且可跨機器分散。多執行緒:共享位址空間,資料可直接傳遞、開銷遠小;代價是缺乏保護,須自行確保資料一致性。C++ 標準只支援多執行緒,未對 IPC 提供原生支援(需平台 API)。
Question 5 - 並發與平行的關注點 [recall]
「並發」與「平行」大部分重疊,那麼兩個詞各自強調的關注點是什麼?
平行注重效能:利用硬體提高資料處理速度時談平行性。並發注重任務分離與任務回應性。兩個術語的存在就是為了區別多執行緒中不同的關注點。
Question 6 - 抽象懲罰 [recall]
什麼是抽象懲罰 (abstraction penalty)?C++ 標準委員會設計執行緒庫時對它的目標是什麼?
抽象懲罰:實現相同功能時,使用高階工具與底層工具之間的開銷差。委員會的目標:高階 API 與底層 API 的效能收益相當,讓標準庫在主流平台都有高效實現(極低抽象懲罰);同時提供原子操作庫等底層工具供追求極致效能者使用。
Question 7 - DVD 播放器的執行緒設計 [application]
你要開發一個 DVD 播放程式,需同時進行影音解碼播放與回應使用者的「暫停/返回選單/退出」操作。單執行緒與多執行緒設計各長什麼樣?你會選哪個?
單執行緒:播放迴圈中必須定期輪詢使用者輸入,「播放」與「UI」代碼被迫耦合。多執行緒(建議):一個執行緒跑 UI、一個跑播放,以分離關注點;UI 執行緒可立即回應(即使只是顯示「請稍候」),兩者只在按下「暫停」等時刻人為關聯。此劃分基於概念設計,執行緒數與 CPU 核心數無關。
Question 8 - 最小多執行緒程式 [application]
寫出一個啟動新執行緒印出訊息的最小 C++ 程式,並說明若省略
t.join()可能發生什麼事。
Question 9 - 任務平行 vs 資料平行 [analysis]
影像處理程式有兩種平行化方案:(a) 把單張圖的濾鏡演算法拆成多個階段並行;(b) 同時對 20 張不同的圖套用相同濾鏡。各屬於哪種平行?哪種通常更容易擴展、為什麼?
(a) 是任務平行(依流程切分單一任務)——通常較複雜,因為各部分間可能存在依賴。(b) 是資料平行的應用(對多組資料同時執行相同操作),著重吞吐量:每張圖耗時不變,但相同時間處理更多資料。(b) 通常更接近易並行 (embarrassingly parallel):圖與圖之間無依賴,可隨硬體執行緒數擴展;(a) 若含不易並行的部分,只能切成固定數量的任務,擴展性受限。
Question 10 - 每連線一執行緒的極限 [analysis]
一台 32-bit 伺服器對每個客戶端連線啟動一條執行緒(每條 1MB 堆疊)。分析連線數成長到數千時會發生什麼問題,並提出改善方向。
問題:(1) 4GB 位址空間 ÷ 1MB 堆疊 → 約 4096 條執行緒就耗盡全部位址空間(代碼、靜態資料、堆積無處可放);(2) 執行緒越多,context switch 越頻繁,整體效能反而下降;(3) 每條執行緒的啟動開銷與 OS 管理資源都在累積。改善:使用執行緒池限制執行緒數量、依硬體並發能力調整執行緒數(參見第9章);但執行緒池不是靈丹妙藥,仍需整體設計配合。64-bit 系統無此位址空間上限,但其他資源依然有限。
| 關鍵字 | 答案 |
|---|---|
| 並發定義 | 兩個以上獨立活動同時發生 |
| 單核多工 | 任務切換 → 並發假象 |
| 多核真平行 | 硬體並發;上限 = 硬體執行緒數 |
| Context switch | 保存狀態/指令指標 → 載入新任務 → 快取重填延遲 |
| 行程 vs 執行緒 | IPC 複雜慢+有保護 vs 共享位址空間+需自保資料一致 |
| C++ 標準的立場 | 只支援多執行緒,IPC 靠平台 API |
| SoC | UI 執行緒 + 工作執行緒;執行緒數依概念設計 |
| 任務平行/資料平行 | 拆演算法/拆資料;吞吐量型 = 更大的問題 |
| 不用並發 | 收益 < 成本(啟動、堆疊、切換、複雜度) |
| C++98 → C++11 → 14/17 | 無執行緒 → 記憶體模型+執行緒庫 → 新互斥鎖+平行演算法 |
| 抽象懲罰 | 高階 vs 底層開銷差,目標極低 |
join() |
等待新執行緒結束,否則 main 可能先跑完 |