การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง
เรียกใช้งานที่ไม่ขึ้นต่อกันพร้อมกันด้วยโหมดขนานของ xargs และกลุ่มงานเบื้องหลังที่จัดการอย่างเป็นระบบ
การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง เป็นบทเรียน Linux Command Line & Bash Scripting Mastery ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Linux Command Line & Bash Scripting Mastery และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Linux Command Line & Bash Scripting Mastery มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดการทำงานตามลำดับจึงช้า
เมื่อคุณเรียกใช้คำสั่งทีละคำสั่งในสคริปต์เชลล์ คุณจะปล่อยให้แกนประมวลผลของ CPU ว่างอยู่ ลองพิจารณาการปรับขนาดรูปภาพ 500 รูป: การเรียกใช้ convert แต่ละครั้งใช้หนึ่งแกน ขณะที่อีกเจ็ดแกนไม่ได้ทำงาน
การประมวลผลแบบขนานช่วยแก้ปัญหานี้ด้วยการส่งงานหลายงานไปทำพร้อมกัน เครื่องมือหลักสองอย่างใน Bash ทำให้เรื่องนี้ทำได้ง่าย:
- xargs -P — กระจายรายการอินพุตไปยังโพรเซสผู้ปฏิบัติงานแบบขนานจำนวน N โพรเซส
- งานเบื้องหลัง (&) + wait — สร้างโพรเซสด้วยตนเองและรอรับผลลัพธ์
บทเรียนนี้ครอบคลุมทั้งสองแนวทาง เพื่อให้คุณเลือกเครื่องมือที่เหมาะสมกับแต่ละสถานการณ์ได้
ทบทวนพื้นฐาน xargs
ก่อนเพิ่มการประมวลผลแบบขนาน ขอทบทวนการทำงานของ xargs กันก่อน โดยจะอ่านรายการต่าง ๆ จากอินพุตมาตรฐาน แล้วส่งรายการเหล่านั้นเป็นอาร์กิวเมนต์ให้คำสั่ง
แฟล็ก -I {} ช่วยให้คุณวางรายการอินพุตไว้ที่ใดก็ได้ในสตริงคำสั่ง ไม่จำเป็นต้องอยู่ท้ายคำสั่งเท่านั้น
ตัวอย่างด้านล่างแปลงไฟล์ .txt ทุกไฟล์เป็นตัวพิมพ์ใหญ่โดยใช้ tr แต่ละไฟล์จะถูกประมวลผลทีละไฟล์ (พื้นฐานแบบทำงานตามลำดับ)
#!/usr/bin/env bash
# Create sample files
mkdir -p /tmp/xargs_demo
for i in 1 2 3; do
echo "hello world $i" > /tmp/xargs_demo/file$i.txt
done
# Process files one at a time (sequential)
find /tmp/xargs_demo -name '*.txt' | xargs -I {} sh -c 'tr a-z A-Z < "$1"' _ {}
# Cleanup
rm -rf /tmp/xargs_demoแนะนำ xargs -P
เพิ่มแฟล็ก -P N ให้กับ xargs เพื่อเรียกใช้ N โพรเซสแบบขนานได้สูงสุด xargs จะจัดการกลุ่มโพรเซสผู้ปฏิบัติงานให้โดยอัตโนมัติ — เมื่อช่องหนึ่งว่างลง รายการถัดไปจะเริ่มทำงานทันที
-P 0— สร้างโพรเซสให้มากเท่าจำนวนอินพุต (ควรใช้อย่างระมัดระวังกับรายการขนาดใหญ่)-P 4— ให้มีผู้ปฏิบัติงานทำงานพร้อมกันไม่เกิน 4 ราย-n 1— ส่งรายการอินพุตหนึ่งรายการพอดีต่อโพรเซสที่เรียกใช้ (มักใช้ร่วมกัน)
การใช้ -n 1 -P 4 ร่วมกันเป็นรูปแบบที่พบบ่อยที่สุด: ผู้ปฏิบัติงานแต่ละรายรับหนึ่งรายการ และมีผู้ปฏิบัติงานพร้อมกันสี่ราย
#!/usr/bin/env bash
# Simulate 8 tasks, each taking ~1 second
# Sequential would take ~8s; parallel with -P 4 takes ~2s
process_item() {
local item="$1"
sleep 1
echo "Done: $item"
}
export -f process_item
time printf '%s\n' task{1..8} | xargs -n 1 -P 4 bash -c 'process_item "$@"' _การประมวลผลไฟล์แบบขนาน
กรณีใช้งานจริงอย่างหนึ่งคือการบีบอัดไฟล์บันทึกจำนวนมากพร้อมกัน หากไม่มี -P การเรียกใช้ gzip แต่ละครั้งจะทำให้รายการถัดไปต้องรอ แต่เมื่อใช้ -P 8 จะบีบอัดไฟล์ได้พร้อมกันสูงสุดแปดไฟล์ ทำให้ใช้แกน CPU ได้เต็มที่
สังเกตว่า -n 1 ช่วยให้ผู้ปฏิบัติงานแบบขนานแต่ละรายได้รับชื่อไฟล์เพียงหนึ่งชื่อพอดี ซึ่งสำคัญมากเมื่อชื่อไฟล์อาจมีช่องว่าง (ใช้คู่กับ -d '\n' หรือ -print0 / -0 เพื่อความปลอดภัย)
#!/usr/bin/env bash
# Create dummy log files
mkdir -p /tmp/logs_demo
for i in $(seq 1 12); do
dd if=/dev/urandom bs=1K count=64 2>/dev/null > /tmp/logs_demo/app_$i.log
done
echo "Files before: $(ls /tmp/logs_demo | wc -l)"
# Compress all .log files in parallel (up to 8 workers)
find /tmp/logs_demo -name '*.log' -print0 \
| xargs -0 -n 1 -P 8 gzip --fast
echo "Files after : $(ls /tmp/logs_demo | wc -l)"
rm -rf /tmp/logs_demoการเลือกค่า -P ที่เหมาะสม
การตั้งค่า -P ต่ำเกินไปทำให้เสียโอกาสใช้แกนประมวลผล ส่วนการตั้งค่าสูงเกินไปทำให้เกิดการแย่งทรัพยากร จุดเริ่มต้นที่ดีคือจำนวนแกน CPU เชิงตรรกะ:
- งานที่ใช้ CPU เป็นหลัก (การบีบอัด การเข้ารหัส):
-P $(nproc) - งานที่ใช้ I/O เป็นหลัก (การเรียกเครือข่าย การอ่านดิสก์):
-P $(($(nproc) * 4))หรือสูงกว่า เนื่องจากผู้ปฏิบัติงานใช้เวลาส่วนใหญ่รอ - งานที่จำกัดด้วยหน่วยความจำ: คำนวณ
available_RAM / task_RAM_usageแล้วจำกัดจำนวนไว้เท่านั้น
nproc จะคืนค่าจำนวนหน่วยประมวลผลที่พร้อมใช้งาน จึงเป็นทางเลือกที่พกพาได้แทนตัวเลขที่กำหนดตายตัว
#!/usr/bin/env bash
CORES=$(nproc)
IO_WORKERS=$(( CORES * 4 ))
echo "CPU cores : $CORES"
echo "CPU-bound -P: $CORES"
echo "I/O-bound -P: $IO_WORKERS"
# Example: parallel curl downloads (I/O-bound)
# printf '%s\n' url1 url2 ... | xargs -n 1 -P "$IO_WORKERS" curl -sSOงานเบื้องหลังด้วย &
บางครั้งคุณต้องการการควบคุมมากกว่าที่ xargs มีให้ เช่น การจัดการข้อผิดพลาดแยกตามงาน รายการแบบเปลี่ยนแปลงได้ หรือรูปแบบอาร์กิวเมนต์ที่ซับซ้อน ให้ใช้ ตัวดำเนินการงานเบื้องหลัง & ที่มีอยู่ในเชลล์เพื่อสร้างงานด้วยตนเอง
การเติม & ต่อท้ายคำสั่งใด ๆ จะคืนการควบคุมให้สคริปต์ทันที โพรเซสลูกจะทำงานเบื้องหลัง ขณะที่โพรเซสแม่ทำงานต่อ เมื่อสิ้นสุด ให้เรียกใช้ wait เพื่อหยุดรอจนกว่าโพรเซสลูกทั้งหมดจะทำงานเสร็จ
#!/usr/bin/env bash
process() {
local id="$1"
sleep $(( RANDOM % 3 + 1 ))
echo "Job $id finished at $(date +%T)"
}
echo "Launching 5 background jobs..."
for id in $(seq 1 5); do
process "$id" &
done
wait # Block until every background job completes
echo "All jobs done."จำกัดการทำงานพร้อมกันด้วยกลุ่มงาน
การสร้างงานทั้งหมดพร้อมกันด้วย & อาจใช้หน่วยความจำจนหมดเมื่อรายการมีขนาดใหญ่ กลุ่มงานจะจำกัดให้มีงานทำงานพร้อมกันไม่เกิน N งาน:
- หลังสร้างงานแต่ละงาน ให้ตรวจสอบจำนวนงานเบื้องหลังที่กำลังทำงานอยู่ด้วย
jobs -r | wc -l - หากจำนวนถึงขีดจำกัด ให้เรียกใช้
wait -n(Bash 4.3 ขึ้นไป) เพื่อรอให้งาน ใดงานหนึ่ง เสร็จก่อนสร้างงานถัดไป
รูปแบบนี้เลียนแบบสิ่งที่ xargs -P ทำอยู่ภายใน แต่ยังให้ความยืดหยุ่นเต็มที่ในการเขียนสคริปต์จัดการแต่ละงาน
#!/usr/bin/env bash
MAX_JOBS=3
process() {
local id="$1"
sleep $(( RANDOM % 3 + 1 ))
echo "Task $id done"
}
for id in $(seq 1 10); do
# Throttle: wait for a slot if pool is full
while (( $(jobs -r | wc -l) >= MAX_JOBS )); do
wait -n 2>/dev/null || true
done
process "$id" &
done
wait
echo "All 10 tasks complete."การเก็บรหัสจบการทำงานจากงานแบบขนาน
ข้อควรระวังสำคัญของงานเบื้องหลังคือ หากโพรเซสลูกล้มเหลว สคริปต์โพรเซสแม่จะไม่ทราบโดยอัตโนมัติ คุณต้องเก็บ PID ของโพรเซสลูกแต่ละตัว และตรวจสอบสถานะการจบการทำงานด้วย wait <pid>
รูปแบบด้านล่างเก็บ PID ทุกตัวไว้ในอาร์เรย์ จากนั้นวนซ้ำในอาร์เรย์และเรียกใช้ wait "$pid" ซึ่งจะคืนค่ารหัสจบการทำงานของโพรเซสลูกตัวนั้นโดยเฉพาะ
#!/usr/bin/env bash
set -euo pipefail
task() {
local id="$1"
sleep 1
if (( id == 3 )); then
echo "Task $id: FAILED" >&2
return 1
fi
echo "Task $id: ok"
}
pids=()
for id in $(seq 1 5); do
task "$id" &
pids+=("$!")
done
failed=0
for pid in "${pids[@]}"; do
if ! wait "$pid"; then
echo "PID $pid exited with error" >&2
(( failed++ ))
fi
done
(( failed == 0 )) && echo "All OK" || { echo "$failed job(s) failed"; exit 1; }การดาวน์โหลดแบบขนานด้วย xargs -P
I/O เครือข่ายเป็นกรณีตัวอย่างที่เหมาะกับการประมวลผลแบบขนานจำนวนมาก — ผู้ปฏิบัติงานแต่ละรายใช้เวลาส่วนใหญ่รอข้อมูล การเรียกใช้ตัวอย่างด้านล่างจะดึงข้อมูล URL หลายรายการพร้อมกัน และบันทึกแต่ละรายการเป็นไฟล์ที่มีชื่อไม่ซ้ำกัน
แฟล็กสำคัญที่ใช้:
-P 8— โพรเซส curl แปดโพรเซสทำงานพร้อมกัน-n 1— หนึ่ง URL ต่อการเรียกใช้ curl หนึ่งครั้ง--create-dirs -o— curl บันทึกลงในชื่อไฟล์ที่สร้างขึ้น
#!/usr/bin/env bash
# Download several small public files in parallel
URLs=(
"https://httpbin.org/bytes/1024"
"https://httpbin.org/bytes/2048"
"https://httpbin.org/bytes/512"
"https://httpbin.org/bytes/4096"
)
mkdir -p /tmp/parallel_dl
printf '%s\n' "${URLs[@]}" | xargs -n 1 -P 4 bash -c '
url="$1"
out="/tmp/parallel_dl/$(echo "$url" | md5sum | cut -c1-8).bin"
curl -sSf "$url" -o "$out" && echo "Saved $out"
' _
ls -lh /tmp/parallel_dl/
rm -rf /tmp/parallel_dlการรวม find, xargs -P และฟังก์ชันเชลล์
หากต้องการใช้ฟังก์ชันเชลล์ที่มีหลายบรรทัดร่วมกับ xargs คุณต้อง ส่งออกฟังก์ชัน ด้วย export -f function_name จากนั้นเรียกใช้ผ่าน bash -c 'function_name "$@"' _ ภายใน xargs
รูปแบบนี้เปิดให้ใช้ความสามารถของการเขียนสคริปต์ได้อย่างเต็มที่ภายในผู้ปฏิบัติงานแบบขนานแต่ละราย ทั้งการบันทึกข้อมูล การจัดการข้อผิดพลาด และตรรกะแบบมีเงื่อนไข — แยกตามรายการ
#!/usr/bin/env bash
mkdir -p /tmp/proc_demo
for i in $(seq 1 8); do echo "data $i" > /tmp/proc_demo/item_$i.txt; done
process_file() {
local f="$1"
local base
base=$(basename "$f" .txt)
# Simulate work: count words and append a timestamp
local wc
wc=$(wc -w < "$f")
echo "[$base] words=$wc processed=$(date +%T)" >> "/tmp/proc_demo/${base}.result"
}
export -f process_file
find /tmp/proc_demo -name '*.txt' -print0 \
| xargs -0 -n 1 -P "$(nproc)" bash -c 'process_file "$@"' _
grep '' /tmp/proc_demo/*.result
rm -rf /tmp/proc_demoการวัดความเร็วที่เพิ่มขึ้นด้วย time
ควรวัดผลก่อนอ้างว่าปรับปรุงได้สำเร็จเสมอ ครอบคำสั่งแบบขนานด้วย time แล้วเปรียบเทียบกับพื้นฐานแบบทำงานตามลำดับ ความเร็วที่เพิ่มขึ้นจริงขึ้นอยู่กับ:
- ความเป็นอิสระของงาน — งานต้องไม่ใช้สถานะที่เขียนแก้ไขได้ร่วมกันโดยไม่มีล็อก
- ค่าใช้จ่ายส่วนเกิน — ต้นทุนการสร้างโพรเซส (ประมาณ 5–20 มิลลิวินาทีต่อโพรเซส) มีความสำคัญกับงานขนาดเล็กมาก
- การแย่งใช้ทรัพยากร — I/O ดิสก์อาจเต็มขีดจำกัดได้ก่อนที่ CPU จะเต็ม
ตัวอย่างรูปแบบการทดสอบประสิทธิภาพอย่างง่ายแสดงอยู่ด้านล่าง — เรียกใช้แบบตามลำดับก่อน จากนั้นแบบขนาน แล้วเปรียบเทียบเวลา real ตามนาฬิกาจริง
#!/usr/bin/env bash
work() { sleep 0.2; } # simulate a 200ms task
export -f work
ITEMS=$(seq 1 16)
echo "=== Sequential ==="
time printf '%s\n' $ITEMS | xargs -n 1 bash -c 'work' _
echo
echo "=== Parallel ($(nproc) workers) ==="
time printf '%s\n' $ITEMS | xargs -n 1 -P "$(nproc)" bash -c 'work' _ตรวจสอบความรู้: การทำงานของ xargs -P
ทดสอบความเข้าใจเกี่ยวกับการทำงานแบบขนานด้วย xargs -P
ทบทวนบทเรียน
ขณะนี้คุณมีเทคนิคที่เชื่อถือได้สองวิธีสำหรับการทำงานแบบขนานใน Bash:
- xargs -n 1 -P N — แนวทางที่ง่ายที่สุด โดย xargs จะจัดการกลุ่มผู้ปฏิบัติงานให้อัตโนมัติ เหมาะที่สุดเมื่ออินพุตเป็นรายการทั่วไป และแต่ละรายการสอดคล้องกับคำสั่งหนึ่งคำสั่ง
- งานเบื้องหลัง (&) + wait — ควบคุมการเขียนสคริปต์ได้อย่างเต็มที่ จำเป็นเมื่อคุณต้องการ PID แยกตามงาน อินพุตแบบเปลี่ยนแปลงได้ หรือการจัดการรหัสจบการทำงานอย่างละเอียด ใช้
wait -nร่วมกับตัวนับเพื่อจำกัดการทำงานพร้อมกัน
กฎสำคัญที่ควรนำไปใช้ต่อ:
- ส่งออกฟังก์ชันเชลล์ด้วย
export -fก่อนส่งฟังก์ชันผ่านxargs - ใช้
-print0/-0เพื่อจัดการชื่อไฟล์ที่มีช่องว่างอย่างปลอดภัย - เก็บ PID ไว้ในอาร์เรย์ และเรียกใช้
wait "$pid"แยกทีละตัวเพื่อตรวจจับความล้มเหลว - ทดสอบประสิทธิภาพด้วย
time— การทำงานแบบขนานจะให้ผลดีเฉพาะเมื่อต้นทุนของงานสูงกว่าต้นทุนการสร้างโพรเซส - ตั้งค่า
-P $(nproc)สำหรับงานที่ใช้ CPU เป็นหลัก และใช้ค่าทวีคูณที่สูงกว่าสำหรับงานที่ใช้ I/O เป็นหลัก
คำถามที่พบบ่อย
บทเรียน “การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Linux Command Line & Bash Scripting Mastery ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Linux Command Line & Bash Scripting Mastery มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง”
เรียกใช้งานที่ไม่ขึ้นต่อกันพร้อมกันด้วยโหมดขนานของ xargs และกลุ่มงานเบื้องหลังที่จัดการอย่างเป็นระบบ คุณปฏิบัติ Linux Command Line & Bash Scripting Mastery ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Linux Command Line & Bash Scripting Mastery หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Linux Command Line & Bash Scripting Mastery บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Linux Command Line & Bash Scripting Mastery นี้ได้ไหม
ได้ บทเรียน Linux Command Line & Bash Scripting Mastery ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การวิเคราะห์ประสิทธิภาพสคริปต์และการหลีกเลี่ยง subshell ที่ไม่จำเป็น
- การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง
- การจัดการเวิร์กโหลดด้วย GNU parallel
- ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล