核心觀點#

控制低階多執行緒程式碼是極其困難的任務。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-reducefilter-reduce 技術:

  • Java:Collection.parallelStream() 方法
  • C++:QtConcurrent 函式庫
  • 大規模任務:Apache Hadoop 等分散式處理框架

只需使用 container 和相容的 map、filter、reduce 函式。

方法四:使用平行化函式庫#

許多計算密集型任務已有針對多核心最佳化的函式庫

  • CPU 廠商(AMD、Intel)提供音視訊編解碼、信號處理、加密、壓縮等函式庫
  • ATLAS(BLAS 規格的實作):自動調校以匹配硬體能力
  • NAG Numerical Components Library:平行化的數值計算和統計演算法

以 R 語言求解 10000x10000 矩陣反轉為例:

函式庫版本時間
單核心 BLAS3 分 10 秒
多核心最佳化 BLAS1 分 2 秒

方法五:使用適合並行的程式語言#

  • 函數式程式設計天然適合平行化,因為各元件不會互相干擾
  • Java:使用 lambda expressions 和 streams
  • JVM 上的函數式語言:ClojureScala
  • .NET:F#
  • 純函數式語言:Haskell
  • 專為並行設計的語言:Erlang
  • 反應式框架:Vert.x

方法六:使用高階並行原語#

Java java.util.concurrent 套件提供多種高階替代方案:

  • Executor / ExecutorService:取代低階執行緒管理
  • CountDownLatch、CyclicBarrier、Exchanger、Phaser、Semaphore:取代手寫的 synchronized 區塊
  • 並行集合ConcurrentHashMapBlockingQueueCopyOnWriteArrayList 等,取代手動同步化
  • CompletableFuture:組織有複雜依賴關係的平行任務
  • Parallel streams + lambda:表達需要並行執行的 filter 和 map 操作

範例:CompletableFuture 解析 IP 位址#

使用 streams 和 CompletableFuture 平行解析 1,000 個 IP 位址:

實作方式時間
CompletableFuture(100 執行緒)37 秒
循序實作48 分鐘

加速比達到 78 倍,主要歸功於 DNS 查詢的 I/O 等待可以被大量平行化。

重點回顧#

  • 面對難以除錯的低階並行程式碼,考慮用更高階的抽象重寫
  • 策略從高到低:現成中介軟體 > 獨立程序/pipeline > map-reduce > 平行化函式庫 > 函數式語言 > 高階並行原語
  • 這些方法的核心思想是將並行的複雜性委託給經過驗證的軟體,而非自己手動管理執行緒和鎖