実行するタスクが10を超えていますが、システムでは、最大4つのタスクを同時に実行できるように制限されています。
私のタスクは次のように開始できます:myprog taskname
これらのタスクを実行するためのbashシェルスクリプトを作成するにはどうすればよいですか。最も重要なことは、1つのタスクが終了すると、スクリプトは別のタスクをすぐに開始できるため、実行中のタスクのカウントが常に4のままになることです。
実行するタスクが10を超えていますが、システムでは、最大4つのタスクを同時に実行できるように制限されています。
私のタスクは次のように開始できます:myprog taskname
これらのタスクを実行するためのbashシェルスクリプトを作成するにはどうすればよいですか。最も重要なことは、1つのタスクが終了すると、スクリプトは別のタスクをすぐに開始できるため、実行中のタスクのカウントが常に4のままになることです。
使用xargs
:
xargs -P <maximum-number-of-process-at-a-time> -n <arguments-per-process> <command>
詳細はこちら。
GNU Parallel を使用すると、次のことができます。
cat tasks | parallel -j4 myprog
コアが 4 つある場合は、次のようにすることもできます。
cat tasks | parallel myprog
http://git.savannah.gnu.org/cgit/parallel.git/tree/READMEから:
GNU Parallel のフル インストールは次のように簡単です。
./configure && make && make install
root でない場合は、~/bin をパスに追加して、~/bin および ~/share にインストールできます。
./configure --prefix=$HOME && make && make install
または、システムに「make」がない場合は、src/parallel src/sem src/niceload src/sql をパスのディレクトリにコピーするだけです。
並列が必要で、「make」がインストールされていない場合 (システムが古いか、Microsoft Windows である可能性があります):
wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
mv parallel sem dir-in-your-$PATH/bin/
この後、次のことができるはずです。
parallel -j0 ping -nc 3 ::: foss.org.my gnu.org freenetproject.org
これにより、3 つの ping パケットが 3 つの異なるホストに並行して送信され、完了すると出力が出力されます。
簡単な紹介については、紹介ビデオをご覧ください: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
4 つのスクリプトを作成し、それぞれが一定数のタスクを連続して実行することをお勧めします。次に、4 つのスクリプトを並行して開始する別のスクリプトを作成します。たとえば、script1.sh、script2.sh、script3.sh、および script4.sh というスクリプトがある場合、headscript.sh というスクリプトを作成できます。
#!/bin/sh
./script1.sh &
./script2.sh &
./script3.sh &
./script4.sh &
おそらく、シグナルで何か賢いことができるでしょう。
これは概念を説明するためのものであり、完全にテストされていないことに注意してください。
#!/usr/local/bin/bash
this_pid="$$"
jobs_running=0
sleep_pid=
# Catch alarm signals to adjust the number of running jobs
trap 'decrement_jobs' SIGALRM
# When a job finishes, decrement the total and kill the sleep process
decrement_jobs()
{
jobs_running=$(($jobs_running - 1))
if [ -n "${sleep_pid}" ]
then
kill -s SIGKILL "${sleep_pid}"
sleep_pid=
fi
}
# Check to see if the max jobs are running, if so sleep until woken
launch_task()
{
if [ ${jobs_running} -gt 3 ]
then
(
while true
do
sleep 999
done
) &
sleep_pid=$!
wait ${sleep_pid}
fi
# Launch the requested task, signalling the parent upon completion
(
"$@"
kill -s SIGALRM "${this_pid}"
) &
jobs_running=$((${jobs_running} + 1))
}
# Launch all of the tasks, this can be in a loop, etc.
launch_task task1
launch_task tast2
...
launch_task task99
このテスト済みのスクリプトは、一度に 5 つのジョブを実行し、新しいジョブを再起動します (SIGCHLD を取得するとスリープ 10.9 が強制終了されるため)。これのより単純なバージョンでは、直接ポーリングを使用できます (スリープ 10.9 を1スリープしてトラップを取り除きます)。
#!/usr/bin/bash
set -o monitor
trap "pkill -P $$ -f 'sleep 10\.9' >&/dev/null" SIGCHLD
totaljobs=15
numjobs=5
worktime=10
curjobs=0
declare -A pidlist
dojob()
{
slot=$1
time=$(echo "$RANDOM * 10 / 32768" | bc -l)
echo Starting job $slot with args $time
sleep $time &
pidlist[$slot]=`jobs -p %%`
curjobs=$(($curjobs + 1))
totaljobs=$(($totaljobs - 1))
}
# start
while [ $curjobs -lt $numjobs -a $totaljobs -gt 0 ]
do
dojob $curjobs
done
# Poll for jobs to die, restarting while we have them
while [ $totaljobs -gt 0 ]
do
for ((i=0;$i < $curjobs;i++))
do
if ! kill -0 ${pidlist[$i]} >&/dev/null
then
dojob $i
break
fi
done
sleep 10.9 >&/dev/null
done
wait
4つのシェルスクリプトに関する他の回答は、すべてのタスクにほぼ同じ時間がかかると想定し、手動でセットアップする必要があるため、完全には満足できません。しかし、ここで私はそれを改善する方法を示します。
メイン スクリプトは、特定の命名規則に従って、実行可能ファイルへのシンボリック リンクを作成します。例えば、
ln -s executable1 ./01-task.01
最初のプレフィックスはソート用で、サフィックスはバッチを識別します (01-04)。ここで、バッチ番号を入力として取り、次のようなことを行う 4 つのシェル スクリプトを生成します。
for t in $(ls ./*-task.$batch | sort ; do
t
rm t
done
bash でのジョブ プールの実装を見てください: https://github.com/spektom/shell-utils/blob/master/jp.sh
たとえば、多くの URL からダウンロードするときに cURL の最大 3 つのプロセスを実行するには、次のように cURL コマンドをラップできます。
./jp.sh "My Download Pool" 3 curl http://site1/...
./jp.sh "My Download Pool" 3 curl http://site2/...
./jp.sh "My Download Pool" 3 curl http://site3/...
...
このWrite a process pool in Bash で紹介された方法に基づいて変更を加えました。
#!/bin/bash
#set -e # this doesn't work here for some reason
POOL_SIZE=4 # number of workers running in parallel
#######################################################################
# populate jobs #
#######################################################################
declare -a jobs
for (( i = 1988; i < 2019; i++ )); do
jobs+=($i)
done
echo '################################################'
echo ' Launching jobs'
echo '################################################'
parallel() {
local proc procs jobs cur
jobs=("$@") # input jobs array
declare -a procs=() # processes array
cur=0 # current job idx
morework=true
while $morework; do
# if process array size < pool size, try forking a new proc
if [[ "${#procs[@]}" -lt "$POOL_SIZE" ]]; then
if [[ $cur -lt "${#jobs[@]}" ]]; then
proc=${jobs[$cur]}
echo "JOB ID = $cur; JOB = $proc."
###############
# do job here #
###############
sleep 3 &
# add to current running processes
procs+=("$!")
# move to the next job
((cur++))
else
morework=false
continue
fi
fi
for n in "${!procs[@]}"; do
kill -0 "${procs[n]}" 2>/dev/null && continue
# if process is not running anymore, remove from array
unset procs[n]
done
done
wait
}
parallel "${jobs[@]}"