コードのセクションをより効率的にしたいと思います。私はそれを複数のプロセスに分岐させ、一度にではなく、一度に50/100回実行させることを考えています。
例(疑似):
for line in file;
do
foo;
foo2;
foo3;
done
このforループを複数回実行したいです。私はこれがフォークでできることを知っています。こんな感じでしょうか?
while(x <= 50)
parent(child pid)
{
fork child()
}
child
{
do
foo; foo2; foo3;
done
return child_pid()
}
または、私はこれについて間違った方法で考えていますか?
ありがとう!
Bashスクリプト(非対話型)では、デフォルトでJOB CONTROLが無効になっているため、job、fg、bgのコマンドを実行できません。
これが私にとってうまくいくものです:
#!/bin/sh
set -m # Enable Job Control
for i in `seq 30`; do # start 30 jobs in parallel
sleep 3 &
done
# Wait for all parallel jobs to finish
while [ 1 ]; do fg 2> /dev/null; [ $? == 1 ] && break; done
最後の行では、「fg」を使用してバックグラウンドジョブをフォアグラウンドに持っています。これは、fgが1($?== 1)を返すまでループでこれを行います。これは、バックグラウンドジョブがなくなったときに行います。
Bashでの明示的なfork
呼び出しについては知りません。おそらくやりたいことは、バックグラウンドで実行したいコマンドに&
を追加することです。 bashスクリプト内で定義する関数で&
を使用することもできます。
do_something_with_line()
{
line=$1
foo
foo2
foo3
}
for line in file
do
do_something_with_line $line &
done
[〜#〜] edit [〜#〜]:同時バックグラウンドプロセスの数を制限するには、次のようなものを試すことができます。
for line in file
do
while [`jobs | wc -l` -ge 50 ]
do
sleep 5
done
do_something_with_line $line &
done
GNU Parallelを使用すると、次のことができます。
cat file | parallel 'foo {}; foo2 {}; foo3 {}'
これにより、各CPUコアで1つのジョブが実行されます。 50を実行するには:
cat file | parallel -j 50 'foo {}; foo2 {}; foo3 {}'
詳細については、紹介ビデオをご覧ください。
プロセスが終了するまでブロックされるので、wait
を使用するのは好きではありません。現在のプロセスが完了するまでステータスの更新を取得できないため、待機するプロセスが複数ある場合は理想的ではありません。これには_kill -0
_とsleep
の組み合わせを使用することを好みます。
待機するpids
の配列が与えられた場合、以下のwaitPids()
関数を使用して、どのpidがまだ保留中かについて継続的なフィードバックを取得します。
_declare -a pids
waitPids() {
while [ ${#pids[@]} -ne 0 ]; do
echo "Waiting for pids: ${pids[@]}"
local range=$(eval echo {0..$((${#pids[@]}-1))})
local i
for i in $range; do
if ! kill -0 ${pids[$i]} 2> /dev/null; then
echo "Done -- ${pids[$i]}"
unset pids[$i]
fi
done
pids=("${pids[@]}") # Expunge nulls created by unset.
sleep 1
done
echo "Done!"
}
_
バックグラウンドでプロセスを開始すると、以下のユーティリティ関数を使用して、そのpidをpids
配列にすぐに追加します。
_addPid() {
desc=$1
pid=$2
echo "$desc -- $pid"
pids=(${pids[@]} $pid)
}
_
使用方法を示すサンプルを次に示します。
_for i in {2..5}; do
sleep $i &
addPid "Sleep for $i" $!
done
waitPids
_
そして、フィードバックは次のようになります。
_Sleep for 2 -- 36271
Sleep for 3 -- 36272
Sleep for 4 -- 36273
Sleep for 5 -- 36274
Waiting for pids: 36271 36272 36273 36274
Waiting for pids: 36271 36272 36273 36274
Waiting for pids: 36271 36272 36273 36274
Done -- 36271
Waiting for pids: 36272 36273 36274
Done -- 36272
Waiting for pids: 36273 36274
Done -- 36273
Waiting for pids: 36274
Done -- 36274
Done!
_
haridsvのアプローチは素晴らしく、プロセッサスロットのセットアップを柔軟に実行できるため、多数のプロセスを実行し続け、ジョブの完了時に新しいジョブをサブミットし、全体の負荷を維持できます。以下は、ngrid「ジョブ」の「グリッド」用のnスロットプロセッサ用のharidsvのコードのMODです(シミュレーションモデルのグリッドに使用します)、8つのジョブのテスト出力が続きます、提出、完了、残り
#!/bin/bash
########################################################################
# see haridsv on forking-multi-threaded-processes-bash
# loop over grid, submitting jobs in the background.
# As jobs complete new ones are set going to keep the number running
# up to n as much as possible, until it tapers off at the end.
#
# 8 jobs
ngrid=8
# 3 at a time
n=3
# running counts
running=0
completed=0
# previous values
prunning=0
pcompleted=0
#
########################################################################
# process monitoring functions
#
declare -a pids
#
function checkPids() {
echo ${#pids[@]}
if [ ${#pids[@]} -ne 0 ]
then
echo "Checking for pids: ${pids[@]}"
local range=$(eval echo {0..$((${#pids[@]}-1))})
local i
for i in $range; do
if ! kill -0 ${pids[$i]} 2> /dev/null; then
echo "Done -- ${pids[$i]}"
unset pids[$i]
completed=$(expr $completed + 1)
fi
done
pids=("${pids[@]}") # Expunge nulls created by unset.
running=$((${#pids[@]}))
echo "#PIDS :"$running
fi
}
#
function addPid() {
desc=$1
pid=$2
echo " ${desc} - "$pid
pids=(${pids[@]} $pid)
}
########################################################################
#
# Loop and report when job changes happen,
# keep going until all are completed.
#
idx=0
while [ $completed -lt ${ngrid} ]
do
#
if [ $running -lt $n ] && [ $idx -lt ${ngrid} ]
then
####################################################################
#
# submit a new process if less than n
# are running and we haven't finished...
#
# get desc for process
#
name="job_"${idx}
# background execution
sleep 3 &
addPid $name $!
idx=$(expr $idx + 1)
#
####################################################################
#
fi
#
checkPids
# if something changes...
if [ ${running} -gt ${prunning} ] || \
[ ${completed} -gt ${pcompleted} ]
then
remain=$(expr $ngrid - $completed)
echo " Running: "${running}" Submitted: "${idx}\
" Completed: "$completed" Remaining: "$remain
fi
# save counts to prev values
prunning=${running}
pcompleted=${completed}
#
sleep 1
#
done
#
########################################################################
テスト出力:
job_0 - 75257
1
Checking for pids: 75257
#PIDS :1
Running: 1 Submitted: 1 Completed: 0 Remaining: 8
job_1 - 75262
2
Checking for pids: 75257 75262
#PIDS :2
Running: 2 Submitted: 2 Completed: 0 Remaining: 8
job_2 - 75267
3
Checking for pids: 75257 75262 75267
#PIDS :3
Running: 3 Submitted: 3 Completed: 0 Remaining: 8
3
Checking for pids: 75257 75262 75267
Done -- 75257
#PIDS :2
Running: 2 Submitted: 3 Completed: 1 Remaining: 7
job_3 - 75277
3
Checking for pids: 75262 75267 75277
Done -- 75262
#PIDS :2
Running: 2 Submitted: 4 Completed: 2 Remaining: 6
job_4 - 75283
3
Checking for pids: 75267 75277 75283
Done -- 75267
#PIDS :2
Running: 2 Submitted: 5 Completed: 3 Remaining: 5
job_5 - 75289
3
Checking for pids: 75277 75283 75289
#PIDS :3
Running: 3 Submitted: 6 Completed: 3 Remaining: 5
3
Checking for pids: 75277 75283 75289
Done -- 75277
#PIDS :2
Running: 2 Submitted: 6 Completed: 4 Remaining: 4
job_6 - 75298
3
Checking for pids: 75283 75289 75298
Done -- 75283
#PIDS :2
Running: 2 Submitted: 7 Completed: 5 Remaining: 3
job_7 - 75304
3
Checking for pids: 75289 75298 75304
Done -- 75289
#PIDS :2
Running: 2 Submitted: 8 Completed: 6 Remaining: 2
2
Checking for pids: 75298 75304
#PIDS :2
2
Checking for pids: 75298 75304
Done -- 75298
#PIDS :1
Running: 1 Submitted: 8 Completed: 7 Remaining: 1
1
Checking for pids: 75304
Done -- 75304
#PIDS :0
Running: 0 Submitted: 8 Completed: 8 Remaining: 0
例を試してみましょう
for x in 1 2 3 ; do { echo a $x ; sleep 1 ; echo b $x ; } & done ; sleep 10
jobs
を使用して、実行中のものを確認します。
あなたが共有したものに基づいて、私はこれをまとめることができました:
#!/usr/bin/env bash
VAR1="192.168.1.20 192.168.1.126 192.168.1.36"
for a in $VAR1; do { ssh -t -t $a -l Administrator "Sudo softwareupdate -l"; } & done;
WAITPIDS="$WAITPIDS "$!;...; wait $WAITPIDS
echo "Script has finished"
Exit 1
これは、3台のマシン上のMac上のすべての更新を一度にリストします。後で、ipaddress.txtをCATするときにすべてのマシンのソフトウェア更新を実行するために使用しました
これが私のスレッド制御関数です。
#!/bin/bash
# This function just checks jobs in background, don't do more things.
# if jobs number is lower than MAX, then return to get more jobs;
# if jobs number is greater or equal to MAX, then wait, until someone finished.
# Usage:
# thread_max 8
# thread_max 0 # wait, until all jobs completed
thread_max() {
local CHECK_INTERVAL="3s"
local CUR_THREADS=
local MAX=
[[ $1 ]] && MAX=$1 || return 127
# reset MAX value, 0 is easy to remember
[ $MAX -eq 0 ] && {
MAX=1
DEBUG "waiting for all tasks finish"
}
while true; do
CUR_THREADS=`jobs -p | wc -w`
# workaround about jobs bug. If don't execute it explicitily,
# CUR_THREADS will stick at 1, even no jobs running anymore.
jobs &>/dev/null
DEBUG "current thread amount: $CUR_THREADS"
if [ $CUR_THREADS -ge $MAX ]; then
sleep $CHECK_INTERVAL
else
return 0
fi
done
}