Shell-Script

並行化 Bash FOR 循環

  • September 8, 2020

我一直在嘗試使用 GNU Parallel 並行化以下腳本,特別是三個 FOR 循環實例中的每一個,但一直無法做到。FOR 循環中包含的 4 個命令串聯執行,每個循環大約需要 10 分鐘。

#!/bin/bash

kar='KAR5'
runList='run2 run3 run4'
mkdir normFunc
for run in $runList
do 
 fsl5.0-flirt -in $kar"deformed.nii.gz" -ref normtemp.nii.gz -omat $run".norm1.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
 fsl5.0-flirt -in $run".poststats.nii.gz" -ref $kar"deformed.nii.gz" -omat $run".norm2.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
 fsl5.0-convert_xfm -concat $run".norm1.mat" -omat $run".norm.mat" $run".norm2.mat"
 fsl5.0-flirt -in $run".poststats.nii.gz" -ref normtemp.nii.gz -out $PWD/normFunc/$run".norm.nii.gz" -applyxfm -init $run".norm.mat" -interp trilinear

 rm -f *.mat
done

你為什麼不直接分叉(又名背景)它們?

foo () {
   local run=$1
   fsl5.0-flirt -in $kar"deformed.nii.gz" -ref normtemp.nii.gz -omat $run".norm1.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
   fsl5.0-flirt -in $run".poststats.nii.gz" -ref $kar"deformed.nii.gz" -omat $run".norm2.mat" -bins 256 -cost corratio -searchrx -90 90 -searchry -90 90 -searchrz -90 90 -dof 12 
   fsl5.0-convert_xfm -concat $run".norm1.mat" -omat $run".norm.mat" $run".norm2.mat"
   fsl5.0-flirt -in $run".poststats.nii.gz" -ref normtemp.nii.gz -out $PWD/normFunc/$run".norm.nii.gz" -applyxfm -init $run".norm.mat" -interp trilinear
}

for run in $runList; do foo "$run" & done

如果不清楚,重要的部分在這裡:

for run in $runList; do foo "$run" & done
                                  ^

導致函式在後台的分叉 shell 中執行。那是平行的。

範例任務

task(){
  sleep 0.5; echo "$1";
}

連續執行

for thing in a b c d e f g; do 
  task "$thing"
done

並行執行

for thing in a b c d e f g; do 
 task "$thing" &
done

在 N 個程序批次中並行執行

N=4
(
for thing in a b c d e f g; do 
  ((i=i%N)); ((i++==0)) && wait
  task "$thing" & 
done
)

也可以將 FIFO 用作信號量並使用它們來確保盡快產生新程序並且同時執行不超過 N 個程序。但它需要更多的程式碼。

具有基於 FIFO 的信號量的 N 個程序:

# initialize a semaphore with a given number of tokens
open_sem(){
   mkfifo pipe-$$
   exec 3<>pipe-$$
   rm pipe-$$
   local i=$1
   for((;i>0;i--)); do
       printf %s 000 >&3
   done
}

# run the given command asynchronously and pop/push tokens
run_with_lock(){
   local x
   # this read waits until there is something to read
   read -u 3 -n 3 x && ((0==x)) || exit $x
   (
    ( "$@"; )
   # push the return code of the command to the semaphore
   printf '%.3d' $? >&3
   )&
}

N=4
open_sem $N
for thing in {a..g}; do
   run_with_lock task $thing
done 

解釋:

printf我們通過推送 (= ) 和彈出 (= read) 標記 ( )將文件描述符 3 用作信號量'000'。通過推送已執行任務的返回碼,如果出現問題,我們可以中止。

引用自:https://unix.stackexchange.com/questions/103920