0Pricing
Linux Command Line & Bash Scripting Mastery · บทเรียน

การทำงานแบบขนานด้วย 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การวิเคราะห์ประสิทธิภาพสคริปต์และการหลีกเลี่ยง subshell ที่ไม่จำเป็น
  2. การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง
  3. การจัดการเวิร์กโหลดด้วย GNU parallel
  4. ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล
← กลับไปที่ Linux Command Line & Bash Scripting Mastery