التزامن وتعدد الخيوط في Rust: تقنيات متقدمة

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

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

ابدأ الدورة

التزامن وتعدد الخيوط في Rust: من الأساسيات إلى التقنيات المتقدمة



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



1. تعدد الخيوط الآمن: Send، Sync و Arc<Mutex<T>>



أساس تعدد الخيوط الآمن في Rust هما خاصيتان رئيسيتان (traits): Send و Sync. النوع الذي يطبق Send يمكن نقله بأمان بين الخيوط. النوع الذي يطبق Sync يمكن مشاركته بأمان بين الخيوط عبر المرجع. معظم الأنواع القياسية (مثل i32، String) تطبق كلا الخاصيتين تلقائيًا إذا كانت تحتوي فقط على بيانات Send/Sync.



للوصول المشترك إلى البيانات القابلة للتغيير من عدة خيوط، يُستخدم غالبًا نمط Arc<Mutex<T>>. Arc (عداد المرجع الذري) يسمح بمشاركة الملكية بين الخيوط، بينما Mutex يضمن الاستبعاد المتبادل.



use std::sync::{Arc, Mutex};use std::thread;

fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![];

for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); }

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

println!("Result: {}", *counter.lock().unwrap());}


من المهم التذكر: Mutex في Rust ليس مجرد قفل، بل هو حاوية تحمي البيانات. الحصول على القفل يُرجع مؤشرًا ذكيًا MutexGuard، والذي يحرر القفل تلقائيًا عند الخروج من النطاق. هذا يمنع الخطأ الكلاسيكي "نسيت إلغاء القفل".



نصيحة متقدمة: RwLock بدلاً من Mutex



إذا كانت بياناتك تُقرأ كثيرًا ولكن نادرًا ما تُعدّل، استخدم RwLock. فهو يسمح بقراءة البيانات في وقت واحد من عدة خيوط، ولكنه يمنع الكتابة طالما يوجد قارئ واحد على الأقل. هذا يمكن أن يحسن الأداء بشكل كبير في سيناريوهات "القراء أكثر من الكتّاب".



use std::sync::RwLock;

let data = RwLock::new(vec![1, 2, 3]);

// القراءة — يمكن لعدة خيوط القراءة في وقت واحدlet read_guard = data.read().unwrap();println!("First element: {}", read_guard[0]);drop(read_guard); // تحرير القفل بشكل صريح

// الكتابة — وصول حصريlet mut write_guard = data.write().unwrap();write_guard.push(4);


2. القنوات (Channels): نقل الرسائل بين الخيوط



القنوات هي بديل للذاكرة المشتركة. في Rust، هناك نوعان رئيسيان من القنوات: std::sync::mpsc (منتج متعدد، مستهلك واحد) و crossbeam-channel (منتج متعدد، مستهلك متعدد).



القناة القياسية mpsc مناسبة تمامًا لسيناريوهات "مستهلك واحد - منتجون متعددون". تعمل وفق مبدأ الطابور: يضع المرسلون الرسائل، ويستلمها المستهلك بترتيب FIFO.



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

fn main() { let (tx, rx) = mpsc::channel();

let tx1 = tx.clone(); thread::spawn(move || { tx1.send("رسالة من الخيط 1").unwrap(); });

thread::spawn(move || { tx.send("رسالة من الخيط 2").unwrap(); });

for received in rx { println!("تم الاستلام: {}", received); }}


المدونات

توصيات الكتب