本篇介紹在 Bash shell 中如何使用 wait
等待背景子行程的執行,並取回每個行程執行結果。
在 shell 程式設計中,為了讓程式執行起來更有效率,有時會讓多個子行程(subprocess)以 spawn 的方式放在背景執行,平行處理多項不同的工作,通常將需要等待硬碟 I/O 或網路回應的工作放在背景,可以程式執行的速度加快很多。
而在產生多個子行程之後,我們通常還會需要在程式的某處等待這些工作完成,並且取回執行結果,檢查每個工作是否有執行成功,這個部份就可以使用
wait
。
以下是一些典型的使用範例程式碼。
wait
等待子行程結束
wait
最基本的作用就是等待所有的子行程都結束,然後才繼續執行之後的工作:
#!/bin/bash echo "Start" # 平行處理多個工作 sleep 3 && echo "Job 1 is done" & sleep 2 && echo "Job 2 is done" & sleep 1 && echo "Job 3 is done" & # 等待所有工作完成 wait echo "Done"
這裡我放了三個測試用的指令,讓它們平行在背景執行,當每個工作完成時會輸出一行訊息,我故意安排讓執行時間比較久的工作先執行,觀察它們完成的順序。
執行之後,結果如下:
Start Job 3 is done Job 2 is done Job 1 is done Done
這裡的 Job 1
雖然是第一個執行的,但因為它需要最常的執行時間,所以最慢完成,wait
會在所有工作都結束之後,才繼續執行後續的指令。
取得子行程執行結果
放在背景執行的工作若處理結果不如預期,可能就會影響以後的程式執行,所以在子行程結束之後,通常都需要檢查一下其傳回值,確認是否每個工作都有正常完成。
wait
指令可以在其參數中指定要等待的子行程 ID,這樣的話 wait
就會等待至指定的子行程執行結束,並傳回該子行程的傳回值,以下是一個簡單的範例:
#!/bin/bash sleep 2 && true & # 成功 #sleep 2 && false & # 失敗 PID=$! # 取得上一個背景行程的 ID wait $PID # 等待背景行程執行完畢 # 檢查背景行程的傳回值 if [ $? -eq 0 ]; then echo "Success." else echo "Fail!" fi
Success.
這個範例將 sleep
放在背景執行,然後靠著 Bash 的特殊變數 $!
取得這個背景行程的行程 ID,並將這個行程 ID 傳給 wait
,等待這個背景工作執行完畢,最後再從 Bash 的特殊變數 $?
取得 wait
的傳回值(也就是子行程的傳回值),檢查這個背景工作是否有執行成功。
取得多個子行程執行結果
以下是一個比較複雜一點的範例,在背景工作結束之後,會計算失敗的工作數,判斷所有工作是否都正常完成。
#!/bin/bash # 計算失敗的工作 FAIL=0 # 平行處理多個工作 sleep 3 && echo "Job 1 is done" && true & # 成功 sleep 2 && echo "Job 2 is fail" && false & # 失敗 sleep 1 && echo "Job 3 is done" && true & # 成功 # 對每一個子行程執行 wait for job in `jobs -p` do echo "Wait job: ${job}" wait $job || let FAIL+=1 done # 檢查失敗工作數 if [ $FAIL -eq 0 ]; then echo "All jobs are done." else echo "${FAIL} jobs fail!" fi
Wait job: 3598 Job 3 is done Job 2 is fail Job 1 is done Wait job: 3599 Wait job: 3600 1 jobs fail!
這個範例跟上一個大同小異,不過我們同時執行好幾個背景行程,然後使用 jobs -p
取得所有背景行程的行程 ID,放進 for
迴圈中逐一呼叫 wait
,並將執行失敗的背景行程數目記錄下來。
參考資料:StackOverflow