Skip to main content

什麼是並行處理

當多個彼此不相依的處理(例如向多個外部 API 發出請求、資料庫的匯總處理等)依序執行時,總耗時等於各處理時間的加總。 若能同時執行,總耗時就能縮短為最耗時的那一項處理。 Laravel 的 Concurrency Facade 以簡單的 API 實作這樣的並行執行。
Concurrency Facade 於 Laravel 11 中引入,Laravel 13 仍可繼續使用。 預設驅動使用子 PHP 程序,因此不需額外安裝套件即可運作。

運作機制

Concurrency Facade 會將傳入的閉包序列化後,送至一個隱藏的 Artisan 指令,並以各自獨立的 PHP 程序執行它們。 處理完成後會將回傳值序列化後傳回父程序。 共提供三種驅動:

基本用法

Concurrency::run()

將閉包陣列傳入 run() 方法後,即會並行執行。 回傳值會以陣列形式接收各閉包所回傳的結果。
可透過陣列解構將各結果分別賦值到變數中。 閉包的順序與回傳值的順序一致。

指定驅動

若想使用特定驅動,可透過 driver() 方法指定。
要更改預設驅動,可將設定檔發布,並更新 default 選項。

fork 驅動的用法

fork 驅動比 process 驅動更快,但只能在 PHP 的 CLI 環境(Artisan 指令或佇列 worker)中使用。 無法在 Web 請求中使用。 使用前請先安裝 spatie/fork 套件。
fork 驅動無法在 Web 請求中運作。當你想在 Artisan 指令或佇列 worker 中做並行處理時再使用。

不接收執行結果:Concurrency::defer()

若你並不關心處理的結果,只希望在傳回 HTTP 回應之後,於背景執行,可以使用 defer() 方法。
呼叫 defer() 當下,閉包並不會立即執行。 它會在 HTTP 回應送出給使用者之後,並行地執行。
對於不必讓使用者等待的處理,例如記錄分析資料或預熱快取,defer() 是最佳選擇。

實戰範例:同時呼叫多個外部 API

想像一個電商網站的儀表板,需要從庫存管理 API、業績匯總 API、配送狀態 API 三處取得資訊。

依序執行(改善前)

並行執行(改善後)

若每個 API 花費 1 秒,依序執行需 3 秒,並行執行則約 1 秒即可完成。

同時執行多個資料庫匯總

使用 process 驅動進行資料庫匯總時,每個子程序都會建立新的資料庫連線。 並行數量較多時,請注意資料庫的最大連線數。

測試時的設定

測試環境中可使用 sync 驅動,讓閉包依序執行。 沒有並行執行所需的程序啟動成本,能加速測試。
也可以在 .env.testing 更改預設驅動。

注意事項

閉包會被序列化後傳給子程序。 無法從閉包外部捕獲不可序列化的物件(資料庫連線、檔案 handle、resource 等)。 需要的物件請在閉包內另行建立。
process 驅動有子程序啟動成本,因此當處理本身非常短(低於幾毫秒)時,並行執行未必更快。 對於 HTTP 請求或資料庫匯總等耗時超過 100ms 的處理,並行化才會顯現效果。
fork 驅動會 fork PHP 程序,因此無法在 Web 請求(FPM 或 Apache)中使用。 僅能在 Artisan 指令或佇列 worker 中使用。
若在並行執行過程中發生例外,run() 會將例外重新拋出。 若希望即使個別處理發生例外,其他處理仍能繼續執行,請在閉包內使用 try/catch

下一步

佇列與工作

學習如何將處理透過佇列與工作,非同步地在背景執行
最後修改於 2026年8月2日