ฉันพยายามเขียนไฟล์. shที่รันหลายโปรแกรมพร้อมกัน
ฉันลองสิ่งนี้
prog1
prog2
แต่นั่นจะทำงาน prog1 จากนั้นรอจนกว่า prog1 จะสิ้นสุดลงและจากนั้นเริ่ม prog2 ...
ดังนั้นฉันจะเรียกใช้พวกเขาในแบบคู่ขนานได้อย่างไร
ฉันพยายามเขียนไฟล์. shที่รันหลายโปรแกรมพร้อมกัน
ฉันลองสิ่งนี้
prog1
prog2
แต่นั่นจะทำงาน prog1 จากนั้นรอจนกว่า prog1 จะสิ้นสุดลงและจากนั้นเริ่ม prog2 ...
ดังนั้นฉันจะเรียกใช้พวกเขาในแบบคู่ขนานได้อย่างไร
คำตอบ:
เกี่ยวกับ:
prog1 & prog2 && fg
นี่จะ:
prog1
เริ่มต้นprog2
และเก็บไว้ในเบื้องหน้าctrl-c
เพื่อให้คุณสามารถปิดมันด้วยprog2
คุณจะกลับไปprog1
's เบื้องหน้าctrl-c
เพื่อให้คุณสามารถยังอยู่ใกล้กับprog1
เมื่อprog2
ยกเลิกหรือไม่ คิดว่า node srv.js & cucumberjs
prog1 & prog2 ; fg
นี่คือการใช้อุโมงค์หลาย ssh ในครั้งเดียว หวังว่านี่จะช่วยใครซักคน
prog2
ไม่สามารถทำงานได้ทันทีคุณจะกลับมามีprog1
ส่วนร่วมในเบื้องหน้า หากสิ่งนี้เป็นที่พึงปรารถนาแสดงว่าไม่เป็นไร
prog1 & prog2 && kill $!
วิธีที่จะยุติกระบวนการทั้งสองครั้งคือ
คุณสามารถใช้wait
:
some_command &
P1=$!
other_command &
P2=$!
wait $P1 $P2
มันกำหนด PID โปรแกรมพื้นหลังให้กับตัวแปร ( $!
เป็นกระบวนการเปิดตัวล่าสุด 'PID) จากนั้นwait
คำสั่งจะรอให้พวกเขา เป็นเรื่องที่ดีเพราะถ้าคุณฆ่าสคริปต์มันจะฆ่ากระบวนการด้วย!
#!/usr/bin/env bash ARRAY='cat bat rat' for ARR in $ARRAY do ./run_script1 $ARR & done P1=$! wait $P1 echo "INFO: Execution of all background processes in the for loop has completed.."
${}
เพื่อสอดแทรกเข้าไปในรายการสตริงหรือคล้ายกัน
ด้วย GNU Parallel http://www.gnu.org/software/parallel/มันง่ายเหมือน:
(echo prog1; echo prog2) | parallel
หรือถ้าคุณต้องการ:
parallel ::: prog1 prog2
เรียนรู้เพิ่มเติม:
parallel
มีไวยากรณ์ต่างกัน ตัวอย่างเช่นบน Debian Derivatives moreutils
แพคเกจประกอบด้วยคำสั่งต่าง ๆ ที่เรียกว่าparallel
ซึ่งมีพฤติกรรมแตกต่างกันมาก
parallel
ดีกว่าการใช้&
?
parallel
ดีกว่าเมื่อมีงานมากกว่าคอร์ซึ่งในกรณีนี้&
จะทำงานมากกว่าหนึ่งงานต่อคอร์ในครั้งเดียว ( หลักหลักการเทียบนกพิราบ )
หากคุณต้องการที่จะสามารถเรียกใช้และฆ่าหลาย ๆ โพรเซสได้อย่างง่ายดายctrl-c
นี่คือวิธีที่ฉันชอบ: วางไข่กระบวนการพื้นหลังหลาย ๆ อันใน(…)
subshell และกับดักSIGINT
เพื่อดำเนินการkill 0
ซึ่งจะฆ่าทุกอย่างที่เกิดในกลุ่ม subshell:
(trap 'kill 0' SIGINT; prog1 & prog2 & prog3)
คุณสามารถมีโครงสร้างการดำเนินการกระบวนการที่ซับซ้อนและทุกอย่างจะปิดเพียงครั้งเดียวctrl-c
(ตรวจสอบให้แน่ใจว่ากระบวนการสุดท้ายทำงานในเบื้องหน้าคือไม่รวม&
หลังprog1.3
)
(trap 'kill 0' SIGINT; prog1.1 && prog1.2 & (prog2.1 | prog2.2 || prog2.3) & prog1.3)
xargs -P <n>
ช่วยให้คุณสามารถรัน<n>
คำสั่งแบบขนาน
ในขณะที่-P
เป็นตัวเลือกที่ไม่เป็นมาตรฐานทั้งการใช้งาน GNU (Linux) และ macOS / BSD รองรับ
ตัวอย่างต่อไปนี้:
time xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
ผลลัพธ์ดูเหมือนว่า:
1 # output from 1st command
4 # output from *last* command, which started as soon as the count dropped below 3
2 # output from 2nd command
3 # output from 3rd command
real 0m3.012s
user 0m0.011s
sys 0m0.008s
เวลาแสดงให้เห็นว่าคำสั่งถูกเรียกใช้ในแบบขนาน (คำสั่งสุดท้ายถูกเปิดใช้เฉพาะหลังจากที่ยกเลิกครั้งแรกของ 3 ต้นฉบับ แต่ดำเนินการอย่างรวดเร็ว)
xargs
คำสั่งตัวเองจะไม่กลับมาจนกว่าคำสั่งทั้งหมดได้เสร็จสิ้น แต่คุณสามารถดำเนินการได้ในพื้นหลังโดยยกเลิกกับผู้ประกอบการการควบคุม&
และจากนั้นใช้wait
builtin รอให้ทั้งxargs
คำสั่งให้เสร็จ
{
xargs -P 3 -I {} sh -c 'eval "$1"' - {} <<'EOF'
sleep 1; echo 1
sleep 2; echo 2
sleep 3; echo 3
echo 4
EOF
} &
# Script execution continues here while `xargs` is running
# in the background.
echo "Waiting for commands to finish..."
# Wait for `xargs` to finish, via special variable $!, which contains
# the PID of the most recently started background process.
wait $!
บันทึก:
BSD / macOS xargs
ต้องการให้คุณระบุจำนวนคำสั่งเพื่อให้ทำงานแบบขนานอย่างชัดเจนในขณะที่ GNU xargs
อนุญาตให้คุณระบุ-P 0
ให้ทำงานมากที่สุดในแบบขนาน
ออกจากกระบวนการทำงานแบบขนานมาถึงขณะที่มันจะถูกสร้างขึ้นจึงจะได้รับการบรรณนิทัศน์อันเป็น
parallel
ดังที่ได้กล่าวไว้ในคำตอบของ Ole (ไม่ได้มาพร้อมกับแพลตฟอร์มส่วนใหญ่) ทำให้เป็นอนุกรม (กลุ่ม) เอาท์พุทสิ่งอำนวยความสะดวกแบบต่อกระบวนการและเสนอคุณสมบัติขั้นสูงอื่น ๆ อีกมากมาย#!/bin/bash
prog1 & 2> .errorprog1.log; prog2 & 2> .errorprog2.log
เปลี่ยนเส้นทางข้อผิดพลาดเพื่อแยกบันทึก
prog1 2> .errorprog1.log & prog2 2> .errorprog2.log &
ls notthere1 & 2> .errorprog1.log; ls notthere2 & 2>.errorprog2.log
ลองด้วย: ข้อผิดพลาดไปที่คอนโซลและไฟล์ข้อผิดพลาดทั้งสองนั้นว่างเปล่า ในฐานะที่เป็น @Dennis Williamson พูดว่า&
เป็นตัวคั่น;
ดังนั้น (ก) จะต้องไปที่จุดสิ้นสุดของคำสั่ง (หลังจากการเปลี่ยนเส้นทาง) และ (b) คุณไม่จำเป็นต้องใช้;
:-) ทั้งหมด
มีโปรแกรมที่มีประโยชน์มากที่เรียกว่า nohup
nohup - run a command immune to hangups, with output to a non-tty
nohup
ด้วยตัวเองไม่ได้ทำงานอะไรในพื้นหลังและการใช้nohup
ไม่ใช่ข้อกำหนดหรือข้อกำหนดเบื้องต้นสำหรับการทำงานในพื้นหลัง พวกเขามักจะมีประโยชน์ร่วมกัน แต่เป็นเช่นนี้ไม่ตอบคำถาม
นี่คือฟังก์ชั่นที่ฉันใช้เพื่อเรียกใช้ที่กระบวนการ max n แบบขนาน (n = 4 ในตัวอย่าง):
max_children=4
function parallel {
local time1=$(date +"%H:%M:%S")
local time2=""
# for the sake of the example, I'm using $2 as a description, you may be interested in other description
echo "starting $2 ($time1)..."
"$@" && time2=$(date +"%H:%M:%S") && echo "finishing $2 ($time1 -- $time2)..." &
local my_pid=$$
local children=$(ps -eo ppid | grep -w $my_pid | wc -w)
children=$((children-1))
if [[ $children -ge $max_children ]]; then
wait -n
fi
}
parallel sleep 5
parallel sleep 6
parallel sleep 7
parallel sleep 8
parallel sleep 9
wait
หาก max_children ถูกตั้งค่าเป็นจำนวนแกนฟังก์ชั่นนี้จะพยายามหลีกเลี่ยงแกนกลางที่ไม่ได้ทำงาน
wait -n
ต้องใช้bash
4.3+และจะเปลี่ยนลอจิกเพื่อรอกระบวนการที่ระบุ / โดยนัยใด ๆที่จะยุติ
ฉันมีสถานการณ์ที่คล้ายกันเมื่อเร็ว ๆ นี้ที่ฉันต้องการเรียกใช้หลายโปรแกรมในเวลาเดียวกันเปลี่ยนเส้นทางผลลัพธ์ของพวกเขาไปยังไฟล์บันทึกที่แยกจากกันและรอให้พวกเขาเสร็จสิ้นและฉันก็จบลงด้วยสิ่งดังนี้:
#!/bin/bash
# Add the full path processes to run to the array
PROCESSES_TO_RUN=("/home/joao/Code/test/prog_1/prog1" \
"/home/joao/Code/test/prog_2/prog2")
# You can keep adding processes to the array...
for i in ${PROCESSES_TO_RUN[@]}; do
${i%/*}/./${i##*/} > ${i}.log 2>&1 &
# ${i%/*} -> Get folder name until the /
# ${i##*/} -> Get the filename after the /
done
# Wait for the processes to finish
wait
ที่มา: http://joaoperibeiro.com/execute-multiple-programs-and-redirect-their-outputs-linux/
ผู้จัดการกระบวนการวางไข่
แน่นอนว่าทางเทคนิคคือกระบวนการและโปรแกรมนี้ควรเรียกว่าตัวจัดการการวางไข่จริงๆ แต่นี่เป็นเพียงวิธีการที่ BASH ทำงานเมื่อมันใช้ส้อมแอมเปอร์แซนด์โดยใช้ fork () หรือการเรียกระบบโคลน () ซึ่งโคลนลงในพื้นที่หน่วยความจำแยกต่างหากแทนที่จะเป็นอะไรเช่น pthread_create () ซึ่งจะแชร์หน่วยความจำ หาก BASH สนับสนุนหลัง "ลำดับของการดำเนินการ" แต่ละอันจะทำงานเหมือนกันและอาจเรียกว่าเป็นเธรดดั้งเดิมในขณะที่ได้รับหน่วยความจำที่มีประสิทธิภาพมากขึ้น อย่างไรก็ตามฟังก์ชั่นมันใช้งานได้เหมือนกันแม้ว่าจะยากขึ้นเล็กน้อยเนื่องจากตัวแปร GLOBAL ไม่สามารถใช้ได้ในแต่ละโคลนของผู้ปฏิบัติงานดังนั้นการใช้ไฟล์การสื่อสารระหว่างกระบวนการและเซมาฟอร์ขั้นต้นเพื่อจัดการส่วนที่สำคัญ แน่นอนว่าการตอบโต้จาก BASH นั้นเป็นคำตอบพื้นฐานที่นี่ แต่ฉันรู้สึกราวกับว่าผู้คนรู้ว่า แต่กำลังมองหาวิธีการจัดการสิ่งที่เกิดขึ้นจริง ๆ มากกว่าแค่แยกมันและลืมมันไป สิ่งนี้แสดงให้เห็นถึงวิธีการจัดการกระบวนการแบบแยกส่วนได้ถึง 200 อินสแตนซ์ทั้งหมดที่เข้าถึงทรัพยากรเดียว เห็นได้ชัดว่านี่เป็น overkill แต่ฉันสนุกกับการเขียนดังนั้นฉันจึงยังคง เพิ่มขนาดเทอร์มินัลของคุณตามลำดับ ฉันหวังว่าคุณจะพบว่ามีประโยชน์นี้
ME=$(basename $0)
IPC="/tmp/$ME.ipc" #interprocess communication file (global thread accounting stats)
DBG=/tmp/$ME.log
echo 0 > $IPC #initalize counter
F1=thread
SPAWNED=0
COMPLETE=0
SPAWN=1000 #number of jobs to process
SPEEDFACTOR=1 #dynamically compensates for execution time
THREADLIMIT=50 #maximum concurrent threads
TPS=1 #threads per second delay
THREADCOUNT=0 #number of running threads
SCALE="scale=5" #controls bc's precision
START=$(date +%s) #whence we began
MAXTHREADDUR=6 #maximum thread life span - demo mode
LOWER=$[$THREADLIMIT*100*90/10000] #90% worker utilization threshold
UPPER=$[$THREADLIMIT*100*95/10000] #95% worker utilization threshold
DELTA=10 #initial percent speed change
threadspeed() #dynamically adjust spawn rate based on worker utilization
{
#vaguely assumes thread execution average will be consistent
THREADCOUNT=$(threadcount)
if [ $THREADCOUNT -ge $LOWER ] && [ $THREADCOUNT -le $UPPER ] ;then
echo SPEED HOLD >> $DBG
return
elif [ $THREADCOUNT -lt $LOWER ] ;then
#if maxthread is free speed up
SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1-($DELTA/100))"|bc)
echo SPEED UP $DELTA%>> $DBG
elif [ $THREADCOUNT -gt $UPPER ];then
#if maxthread is active then slow down
SPEEDFACTOR=$(echo "$SCALE;$SPEEDFACTOR*(1+($DELTA/100))"|bc)
DELTA=1 #begin fine grain control
echo SLOW DOWN $DELTA%>> $DBG
fi
echo SPEEDFACTOR $SPEEDFACTOR >> $DBG
#average thread duration (total elapsed time / number of threads completed)
#if threads completed is zero (less than 100), default to maxdelay/2 maxthreads
COMPLETE=$(cat $IPC)
if [ -z $COMPLETE ];then
echo BAD IPC READ ============================================== >> $DBG
return
fi
#echo Threads COMPLETE $COMPLETE >> $DBG
if [ $COMPLETE -lt 100 ];then
AVGTHREAD=$(echo "$SCALE;$MAXTHREADDUR/2"|bc)
else
ELAPSED=$[$(date +%s)-$START]
#echo Elapsed Time $ELAPSED >> $DBG
AVGTHREAD=$(echo "$SCALE;$ELAPSED/$COMPLETE*$THREADLIMIT"|bc)
fi
echo AVGTHREAD Duration is $AVGTHREAD >> $DBG
#calculate timing to achieve spawning each workers fast enough
# to utilize threadlimit - average time it takes to complete one thread / max number of threads
TPS=$(echo "$SCALE;($AVGTHREAD/$THREADLIMIT)*$SPEEDFACTOR"|bc)
#TPS=$(echo "$SCALE;$AVGTHREAD/$THREADLIMIT"|bc) # maintains pretty good
#echo TPS $TPS >> $DBG
}
function plot()
{
echo -en \\033[${2}\;${1}H
if [ -n "$3" ];then
if [[ $4 = "good" ]];then
echo -en "\\033[1;32m"
elif [[ $4 = "warn" ]];then
echo -en "\\033[1;33m"
elif [[ $4 = "fail" ]];then
echo -en "\\033[1;31m"
elif [[ $4 = "crit" ]];then
echo -en "\\033[1;31;4m"
fi
fi
echo -n "$3"
echo -en "\\033[0;39m"
}
trackthread() #displays thread status
{
WORKERID=$1
THREADID=$2
ACTION=$3 #setactive | setfree | update
AGE=$4
TS=$(date +%s)
COL=$[(($WORKERID-1)/50)*40]
ROW=$[(($WORKERID-1)%50)+1]
case $ACTION in
"setactive" )
touch /tmp/$ME.$F1$WORKERID #redundant - see main loop
#echo created file $ME.$F1$WORKERID >> $DBG
plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID INIT " good
;;
"update" )
plot $COL $ROW "Worker$WORKERID: ACTIVE-TID:$THREADID AGE:$AGE" warn
;;
"setfree" )
plot $COL $ROW "Worker$WORKERID: FREE " fail
rm /tmp/$ME.$F1$WORKERID
;;
* )
;;
esac
}
getfreeworkerid()
{
for i in $(seq 1 $[$THREADLIMIT+1])
do
if [ ! -e /tmp/$ME.$F1$i ];then
#echo "getfreeworkerid returned $i" >> $DBG
break
fi
done
if [ $i -eq $[$THREADLIMIT+1] ];then
#echo "no free threads" >> $DBG
echo 0
#exit
else
echo $i
fi
}
updateIPC()
{
COMPLETE=$(cat $IPC) #read IPC
COMPLETE=$[$COMPLETE+1] #increment IPC
echo $COMPLETE > $IPC #write back to IPC
}
worker()
{
WORKERID=$1
THREADID=$2
#echo "new worker WORKERID:$WORKERID THREADID:$THREADID" >> $DBG
#accessing common terminal requires critical blocking section
(flock -x -w 10 201
trackthread $WORKERID $THREADID setactive
)201>/tmp/$ME.lock
let "RND = $RANDOM % $MAXTHREADDUR +1"
for s in $(seq 1 $RND) #simulate random lifespan
do
sleep 1;
(flock -x -w 10 201
trackthread $WORKERID $THREADID update $s
)201>/tmp/$ME.lock
done
(flock -x -w 10 201
trackthread $WORKERID $THREADID setfree
)201>/tmp/$ME.lock
(flock -x -w 10 201
updateIPC
)201>/tmp/$ME.lock
}
threadcount()
{
TC=$(ls /tmp/$ME.$F1* 2> /dev/null | wc -l)
#echo threadcount is $TC >> $DBG
THREADCOUNT=$TC
echo $TC
}
status()
{
#summary status line
COMPLETE=$(cat $IPC)
plot 1 $[$THREADLIMIT+2] "WORKERS $(threadcount)/$THREADLIMIT SPAWNED $SPAWNED/$SPAWN COMPLETE $COMPLETE/$SPAWN SF=$SPEEDFACTOR TIMING=$TPS"
echo -en '\033[K' #clear to end of line
}
function main()
{
while [ $SPAWNED -lt $SPAWN ]
do
while [ $(threadcount) -lt $THREADLIMIT ] && [ $SPAWNED -lt $SPAWN ]
do
WID=$(getfreeworkerid)
worker $WID $SPAWNED &
touch /tmp/$ME.$F1$WID #if this loops faster than file creation in the worker thread it steps on itself, thread tracking is best in main loop
SPAWNED=$[$SPAWNED+1]
(flock -x -w 10 201
status
)201>/tmp/$ME.lock
sleep $TPS
if ((! $[$SPAWNED%100]));then
#rethink thread timing every 100 threads
threadspeed
fi
done
sleep $TPS
done
while [ "$(threadcount)" -gt 0 ]
do
(flock -x -w 10 201
status
)201>/tmp/$ME.lock
sleep 1;
done
status
}
clear
threadspeed
main
wait
status
echo
สคริปต์ของคุณควรมีลักษณะดังนี้:
prog1 &
prog2 &
.
.
progn &
wait
progn+1 &
progn+2 &
.
.
สมมติว่าระบบของคุณสามารถทำงานได้ครั้งละ n งาน ใช้ wait เพื่อรันงานเพียงครั้งละ n งานเท่านั้น
ด้วยbashj ( https://sourceforge.net/projects/bashj/ ) คุณควรจะสามารถทำงานได้หลายกระบวนการ (วิธีที่คนอื่นแนะนำ) แต่ก็สามารถทำได้หลายเธรดใน JVM เดียวที่ควบคุมจากสคริปต์ของคุณ แต่แน่นอนว่าต้องใช้ Java JDK เธรดใช้ทรัพยากรน้อยกว่ากระบวนการ
นี่คือรหัสการทำงาน:
#!/usr/bin/bashj
#!java
public static int cnt=0;
private static void loop() {u.p("java says cnt= "+(cnt++));u.sleep(1.0);}
public static void startThread()
{(new Thread(() -> {while (true) {loop();}})).start();}
#!bashj
j.startThread()
while [ j.cnt -lt 4 ]
do
echo "bash views cnt=" j.cnt
sleep 0.5
done
wait
! ใช่ในทุบตีคุณสามารถรอกระบวนการลูกของสคริปต์