核心觀點#
控制低階多執行緒程式碼是極其困難的任務。Race condition、deadlock、livelock 和次優效能只是常見問題的一部分。當遇到難以解決的並行 bug 時,值得考慮放棄低階並行程式碼,改用更高階的解決方案。
本質上,這些方法是將多核心處理的複雜性委託給其他軟體來處理。
方法一:使用現成的中介軟體#
最高階的方式——找到可靠的 ready-made middleware 來承擔並行工作:
- Web 應用伺服器(如 Java EE、Node.js):自動將工作分配到多執行緒/多程序
- 關聯式資料庫:查詢最佳化引擎能自動將查詢分拆到多核心(例如每個 WHERE 子句分配到一個核心)
- 你只需要讓應用程式在這些框架內執行,讓資料庫盡可能承擔更多工作
方法二:拆分為獨立程序#
讓作業系統替你分配多核心的工作:
Unix Pipeline#
bzip2 -dc <data.bz2 | gzip -c >data.gz循序執行需要 41 秒,pipeline 只需 27 秒——因為解壓縮和壓縮會並行執行在不同核心上。
GNU parallel#
適合處理離散的資料區塊(檔案、行、記錄):
ls *.jpg | parallel 'djpeg -scale 1/16 {} | cjpeg >thumb/{}'用 xargs 循序處理需 10.5 秒,用 parallel 只需 4.1 秒。
Makefile#
用 make -j 進行平行建置:
$ time make >/dev/null # 14 分 18 秒
$ time make -j 8 >/dev/null # 3 分 12 秒
make -j不只適用於編譯——任何可以用依賴關係圖表達的工作流程都能用 Makefile 來平行化。
方法三:Map-Reduce 和 Filter-Reduce#
在應用程式內部使用 map-reduce 和 filter-reduce 技術:
- Java:
Collection.parallelStream()方法 - C++:QtConcurrent 函式庫
- 大規模任務:Apache Hadoop 等分散式處理框架
只需使用 container 和相容的 map、filter、reduce 函式。
方法四:使用平行化函式庫#
許多計算密集型任務已有針對多核心最佳化的函式庫:
- CPU 廠商(AMD、Intel)提供音視訊編解碼、信號處理、加密、壓縮等函式庫
- ATLAS(BLAS 規格的實作):自動調校以匹配硬體能力
- NAG Numerical Components Library:平行化的數值計算和統計演算法
以 R 語言求解 10000x10000 矩陣反轉為例:
| 函式庫版本 | 時間 |
|---|---|
| 單核心 BLAS | 3 分 10 秒 |
| 多核心最佳化 BLAS | 1 分 2 秒 |
方法五:使用適合並行的程式語言#
- 函數式程式設計天然適合平行化,因為各元件不會互相干擾
- Java:使用 lambda expressions 和 streams
- JVM 上的函數式語言:Clojure、Scala
- .NET:F#
- 純函數式語言:Haskell
- 專為並行設計的語言:Erlang
- 反應式框架:Vert.x
方法六:使用高階並行原語#
Java java.util.concurrent 套件提供多種高階替代方案:
- Executor / ExecutorService:取代低階執行緒管理
- CountDownLatch、CyclicBarrier、Exchanger、Phaser、Semaphore:取代手寫的
synchronized區塊 - 並行集合:
ConcurrentHashMap、BlockingQueue、CopyOnWriteArrayList等,取代手動同步化 - CompletableFuture:組織有複雜依賴關係的平行任務
- Parallel streams + lambda:表達需要並行執行的 filter 和 map 操作
範例:CompletableFuture 解析 IP 位址#
使用 streams 和 CompletableFuture 平行解析 1,000 個 IP 位址:
| 實作方式 | 時間 |
|---|---|
| CompletableFuture(100 執行緒) | 37 秒 |
| 循序實作 | 48 分鐘 |
加速比達到 78 倍,主要歸功於 DNS 查詢的 I/O 等待可以被大量平行化。
重點回顧#
- 面對難以除錯的低階並行程式碼,考慮用更高階的抽象重寫
- 策略從高到低:現成中介軟體 > 獨立程序/pipeline > map-reduce > 平行化函式庫 > 函數式語言 > 高階並行原語
- 這些方法的核心思想是將並行的複雜性委託給經過驗證的軟體,而非自己手動管理執行緒和鎖