قوائم المهام في Rust: دليل عملي للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة في قوائم المهام في Rust

\n\n

قوائم المهام هي أحد الأنماط الأساسية للبرمجة المتزامنة. فهي تسمح بتنظيم نقل العمل بكفاءة بين الخيوط، مما يبسط بنية التطبيقات. في Rust، مع نظام الملكية والأنواع الصارم، يتطلب العمل مع قوائم المهام نهجًا خاصًا، لكنه يمنح في المقابل أمانًا وأداءً لا يُصدقان.

\n\n

في هذه المقالة، سنشرح كيفية إنشاء واستخدام قوائم المهام في Rust من الصفر. ستتعرف على الأدوات القياسية للغة، مثل قنوات mpsc (multi-producer, single-consumer)، وستتعرف على المكتبات الشائعة للمعالجة غير المتزامنة. المادة موجهة للمطورين المبتدئين الذين هم على دراية بأساسيات بناء جملة Rust.

\n\n

الأساسيات: ما هي قائمة المهام ولماذا نحتاجها

\n\n

قائمة المهام هي بنية بيانات تعمل وفق مبدأ FIFO (First In, First Out). في سياق تعدد الخيوط، تسمح لبعض الخيوط (المنتجين) بإضافة المهام، ولأخرى (المستهلكين) بأخذها وتنفيذها.

\n\n

المزايا الرئيسية لاستخدام قوائم المهام:

\n\n
    \n
  • فصل المسؤوليات — يتم فصل منطق إنشاء المهام عن تنفيذها.
  • \n
  • موازنة الحمل — يمكن تشغيل عدة خيوط مستهلكة للمعالجة المتوازية.
  • \n
  • التحكم في تدفق البيانات — تعمل القائمة على تخفيف الأحمال الذروة، مما يمنع إرهاق النظام.
  • \n
  • سهولة الاختبار — يمكن اختبار كل مكون بشكل منفصل.
  • \n
\n\n

في Rust، توفر المكتبة القياسية الوحدة النمطية std::sync::mpsc لإنشاء قنوات بمستقبل واحد. هذه أداة مثالية للبدء.

\n\n

إنشاء قائمة مهام بسيطة باستخدام mpsc

\n\n

لنبدأ بمثال أساسي: منتج واحد يرسل أرقامًا، ومستهلك واحد يعالجها.

\n\n
use std::sync::mpsc;\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n    // إنشاء قناة: tx — مرسل، rx — مستقبل\n    let (tx, rx) = mpsc::channel();\n\n    // تشغيل خيط منتج\n    thread::spawn(move || {\n        for i in 1..=5 {\n            println!("جارٍ الإرسال: {}", i);\n            tx.send(i).unwrap();\n            thread::sleep(Duration::from_millis(500));\n        }\n    });\n\n    // خيط مستهلك (الخيط الرئيسي)\n    for received in rx {\n        println!("تم الاستلام: {}", received);\n    }\n}\n
\n\n

في هذا المثال، يمكن استنساخ tx (المرسل) لعدة منتجين، بينما rx (المستقبل) هو المستهلك الوحيد. تقوم طريقة send بإرجاع Result، والذي نعالجه عبر unwrap() — في الكود الحقيقي، يجب معالجة الأخطاء بشكل أفضل.

\n\n

منتجون متعددون

\n\n

لإنشاء عدة خيوط منتجة، نقوم باستنساخ المرسل:

\n\n
use std::sync::mpsc;\nuse std::thread;\n\nfn main() {\n    let (tx, rx) = mpsc::channel();\n    let tx1 = tx.clone();\n\n    thread::spawn(move || {\n        for i in 1..=3 {\n            tx1.send(format!("الخيط 1: رسالة {}", i)).unwrap();\n        }\n    });\n\n    thread::spawn(move || {\n        for i in 1..=3 {\n            tx.send(format!("الخيط 2: رسالة {}", i)).unwrap();\n        }\n    });\n\n    for received in rx {\n        println!("{}", received);\n    }\n}\n
\n\n

لاحظ: يتم نقل tx الأصلي إلى الخيط الثاني، لذلك نقوم باستنساخه قبل النقل.

\n\n

تقنيات متقدمة: أنواع المهام ومعالجة الأخطاء

\n\n

في التطبيقات الحقيقية، نادرًا ما تكون المهام مجرد أرقام. غالبًا ما تكون هياكل تحتوي على بيانات ومنطق. دعنا نرى كيفية تنظيم قائمة مهام بأنواع مختلفة.

\n\n

تعداد لأنواع المهام المختلفة

\n\n
use std::sync::mpsc;\nuse std::thread;\n\n#[derive(Debug)]\nenum Task {\n    Compute { a:                

المدونات

توصيات الكتب