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

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

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

ابدأ الدورة

مقدمة: لماذا نحتاج قوائم المهام في Rust؟

عند تطوير التطبيقات بلغة Rust، غالبًا ما تنشأ الحاجة لمعالجة المهام بشكل غير متزامن أو توزيع الحمل بين عدة خيوط. قائمة المهام (task queue) هي بنية بيانات أساسية تسمح بتنظيم المعالجة المتسلسلة أو المتزامنة لوحدات العمل. في هذه المقالة، سنشرح كيفية إنشاء واستخدام قوائم المهام في Rust، بدءًا من الأمثلة البسيطة وصولاً إلى السيناريوهات الواقعية.

تقدم Rust عدة طرق لتنفيذ قوائم المهام: من القنوات القياسية في std::sync::mpsc إلى الحلول غير المتزامنة المتقدمة القائمة على tokio. سنستعرض كل منها ونوضح كيفية اختيار الأداة المناسبة لمهمتك.



الأساسيات: قائمة مهام عبر قنوات mpsc

أبسط طريقة لإنشاء قائمة مهام في Rust هي استخدام قناة متعددة المنتجين، مستهلك واحد (mpsc) من المكتبة القياسية. هذه الآلية مثالية للحالات التي يعالج فيها خيط واحد المهام المرسلة من عدة خيوط منتجة.



إنشاء قائمة بسيطة

لننظر إلى مثال أساسي حيث يرسل الخيط الرئيسي المهام، ويعالجها خيط منفصل:

use std::sync::mpsc;use std::thread;use std::time::Duration;

fn main() { // إنشاء قناة: مرسل ومستقبل let (tx, rx) = mpsc::channel::<String>();

// تشغيل خيط المعالج let handler = thread::spawn(move || { for task in rx { println!("معالجة المهمة: {}", task); thread::sleep(Duration::from_millis(500)); } });

// إرسال المهام let tasks = vec!["المهمة 1", "المهمة 2", "المهمة 3"]; for task in tasks { tx.send(task.to_string()).unwrap(); println!("تم الإرسال: {}", task); }

// إغلاق القناة لإنهاء المعالج drop(tx); handler.join().unwrap();}

يوضح هذا الكود الآلية الأساسية: tx.send() تضع المهمة في القائمة، ويستخرجها المستقبل rx بترتيب الوصول (FIFO). لاحظ أن القناة تحجب المنتج تلقائيًا إذا كانت القائمة ممتلئة، وتحجب المستهلك إذا كانت القائمة فارغة.



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

من أهم مزايا mpsc إمكانية استنساخ المرسل لعدة خيوط:

use std::sync::mpsc;use std::thread;

fn main() { let (tx, rx) = mpsc::channel::<u32>(); let mut handles = vec![];

// تشغيل 3 خيوط منتجة for id in 0..3 { let tx_clone = tx.clone(); handles.push(thread::spawn(move || { for i in 0..5 { let task = id * 100 + i; tx_clone.send(task).unwrap(); } })); }

// إغلاق المرسل الأصلي drop(tx);

// معالجة جميع المهام for received in rx { println!("تم استلام المهمة: {}", received); }

for handle in handles { handle.join().unwrap(); }}

يرسل كل منتج مهامه، ويعالجها مستهلك واحد بترتيب الوصول. هذا هو النمط الكلاسيكي "منتج-مستهلك".



قائمة مهام متقدمة مع أولويات

القناة القياسية mpsc لا تدعم الأولويات - تتم معالجة المهام بدقة بترتيب الإرسال. لكن يمكننا تنفيذ قائمة مهام ذات أولويات باستخدام BinaryHeap من المكتبة القياسية.



تنفيذ قائمة ذات أولويات

use std::collections::BinaryHeap;use std::cmp::Ordering;

#[derive(Debug, Eq, PartialEq)]struct Task { priority: u8, data: String,}

impl Ord for Task { fn cmp(&self, other: &Self) -> Ordering { // كلما قل الرقم، زادت الأولوية other.priority.cmp(&self

المدونات

توصيات الكتب