0Pricing
Linux Command Line & Bash Scripting Mastery · درس

التوازي باستخدام xargs -P والمهام الخلفية

شغّل المهام المستقلة بالتزامن باستخدام وضع التوازي في xargs ومجموعات المهام الخلفية المُدارة

التوازي باستخدام xargs -P والمهام الخلفية درس مجاني في Linux Command Line & Bash Scripting Mastery على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Linux Command Line & Bash Scripting Mastery، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Linux Command Line & Bash Scripting Mastery 4 دروس في المجموع.

لماذا يكون التنفيذ التسلسلي بطيئًا

عند تشغيل الأوامر واحدًا تلو الآخر في نص shell برمجي، تتركون أنوية المعالج دون استخدام. تأملوا تغيير حجم 500 صورة: يستخدم كل استدعاء لـconvert نواةً واحدة، بينما تبقى الأنوية السبع الأخرى خاملة.

يحلّ التوازي هذه المشكلة عبر توزيع مهام متعددة في الوقت نفسه. وتسهّل أداتان أساسيتان في Bash ذلك:

  • xargs -P — توزّع قائمة من المدخلات على N من عمليات العاملين المتوازية
  • المهام في الخلفية (&) + wait — تنشئون العمليات يدويًا ثم تجمعونها

يغطي هذا الدرس النهجين، لتتمكنوا من اختيار الأداة المناسبة لكل حالة.

مراجعة أساسيات xargs

قبل إضافة التوازي، تذكّروا طريقة عمل xargs. فهي تقرأ العناصر من stdin وتمررها كوسائط إلى أحد الأوامر.

يتيح الخيار -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، تُجرى ثماني عمليات ضغط كحد أقصى في آن واحد، فتُستغل جميع أنوية المعالج.

لاحظوا أن -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 على قيمة منخفضة جدًا إلى إهدار الأنوية، بينما تسبب القيمة المرتفعة جدًا تنافسًا مفرطًا على الموارد. نقطة بداية جيدة هي عدد أنوية المعالج المنطقية:

  • المهام المقيّدة بالمعالج (الضغط والترميز): -P $(nproc)
  • المهام المقيّدة بعمليات الإدخال والإخراج (استدعاءات الشبكة وقراءة القرص): -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

تُعد عمليات الإدخال والإخراج عبر الشبكة حالة نموذجية للتوازي العالي — إذ يقضي كل عامل معظم وقته في انتظار وصول البيانات. يجلب المثال أدناه عدة عناوين 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 مللي ثانية لكل عملية) في المهام الصغيرة
  • التنافس على الموارد — قد تصل عمليات الإدخال والإخراج على القرص إلى حدها الأقصى حتى قبل المعالج

يوضح المثال أدناه نمطًا بسيطًا للاختبار المعياري — شغّلوه تسلسليًا ثم بالتوازي، وقارنوا أزمنة 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 — تحكم كامل في كتابة النص البرمجي؛ وهو ضروري عند الحاجة إلى PIDs لكل مهمة، أو إدخال ديناميكي، أو معالجة دقيقة لرموز الخروج. استخدموا wait -n مع عدّاد لتحديد التزامن الأقصى.

القواعد الأساسية التي ينبغي تذكّرها:

  • صدّروا دوال الصدفة باستخدام export -f قبل تمريرها عبر xargs
  • استخدموا -print0 / -0 للتعامل بأمان مع أسماء الملفات التي تحتوي على مسافات
  • التقطوا PIDs في مصفوفة واستدعوا wait "$pid" لكل عنصر على حدة لاكتشاف حالات الفشل
  • أجروا اختبارًا معياريًا باستخدام time — لا يحقق التوازي فائدة إلا عندما يتجاوز العبء الإضافي للمهمة تكلفة إنشاء العملية
  • اضبطوا -P $(nproc) للمهام المقيّدة بالمعالج، واستخدموا مضاعفًا أعلى لأحمال العمل المقيّدة بعمليات الإدخال والإخراج

الأسئلة الشائعة

هل درس «التوازي باستخدام xargs -P والمهام الخلفية» مجاني؟

نعم — نص درس «التوازي باستخدام xargs -P والمهام الخلفية» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 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 مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تحليل أداء السكربتات وتجنّب subshells غير الضرورية
  2. التوازي باستخدام xargs -P والمهام الخلفية
  3. تنسيق أحمال العمل باستخدام GNU parallel
  4. خطوط الأنابيب المتدفقة والأنابيب المسماة لزيادة الإنتاجية
← العودة إلى Linux Command Line & Bash Scripting Mastery