核心概念#

什麼是時間耦合(temporal coupling)?它關乎「時間」。

時間是軟體架構中常被忽略的面向。我們通常只關注時程表上的時間——離交付還有多久——但這裡要談的是時間在軟體設計中扮演的角色。時間有兩個對我們重要的面向:並行(同時發生的事情)和順序(事情在時間上的相對位置)。

我們通常不會帶著這些觀點來設計程式。當人們坐下來設計架構或寫程式時,思維傾向於線性的——先做這個,然後再做那個。但這種思維導致了時間耦合:方法 A 必須在方法 B 之前呼叫;一次只能跑一份報表;必須等螢幕重繪完才能接收按鈕點擊。Tick 必須發生在 Tock 之前。

這種做法既不靈活,也不切實際。我們需要允許並行,並解耦任何時間或順序上的依賴關係。這樣做可以在工作流程分析、架構、設計和部署等多個層面提高靈活性。

尋找並行機會#

在許多專案中,我們需要建模和分析應用程式的工作流程。我們想找出哪些事情可以同時發生,哪些必須依序進行。一種方法是使用活動圖(activity diagram)這類符號來捕捉工作流程。

Tip 56 - Analyze Workflow to Improve Concurrency(分析工作流程以改善並行性)

活動圖由一組畫在圓角方框裡的動作組成。離開一個動作的箭頭可以指向另一個動作(第一個完成後才開始),或指向一條粗線稱為同步條(synchronization bar)。所有指向同步條的動作都完成後,才能沿著離開同步條的箭頭繼續。沒有箭頭指向的動作可以隨時開始。

以 Pina Colada 為例#

書中以一台自動 pina colada 調酒機為例:需要打開果汁機、倒入材料、量蘭姆酒、加冰塊、攪拌、拿杯子、拿小雨傘、上菜——共 12 個步驟,看似串列。

但用活動圖分析後發現,頂層任務(打開果汁機、打開材料、量蘭姆酒、拿杯子、拿小雨傘)可以全部同時開始。某些後續步驟也可以平行進行。這些優化在 pina colada 製作大賽中可能就是決勝關鍵。

並行的機會#

活動圖顯示了潛在的並行區域,但不代表所有區域都值得開發。例如 pina colada 範例中,調酒師需要五隻手才能同時執行所有初始任務。

設計的重點在這裡。分析活動後我們意識到「攪拌」會花一分鐘,在等待的時間裡,調酒師可以去拿杯子和雨傘,甚至可能還有時間服務另一位客人。

我們在設計並行時,要找的是需要等待時間但不佔用程式碼執行時間的活動——查詢資料庫、存取外部服務、等待使用者輸入。這些通常會讓我們的程式閒置等待,而這些都是做些更有用事情的機會。

平行的機會#

記住區別:並行是軟體機制,平行是硬體層面的。如果我們有多個處理器(本地或遠端),就可以將工作分配給它們以減少整體耗時。

理想的拆分方式是找出相對獨立的工作——每個都可以不等待其他工作就繼續進行。常見模式是將一大塊工作切成獨立的區塊,各自平行處理,再合併結果。

實例: Elixir 編譯器啟動時會將專案拆分成模組,平行編譯每一個。當某個模組依賴另一個時,它的編譯會暫停直到所需模組完成。最終結果是充分利用所有核心的快速編譯。

辨識機會是容易的部分#

我們已經找到了可以從並行和平行中受益的地方。困難的部分是:如何安全地實作它?這是本章後續主題要探討的內容。

相關章節#

  • Topic 10,正交性
  • Topic 26,如何平衡資源
  • Topic 28,去耦合
  • Topic 36,黑板