التزامن وتعدد الخيوط في Rust: كتابة كود آمن وسريع
نادراً ما تعمل البرامج الحديثة في وضع الخيط الواحد. معالجة الطلبات، العمل مع الملفات، العمليات الشبكية — كل هذا يتطلب تنفيذاً متوازياً للمهام. تقدم Rust نهجاً فريداً للتزامن: فهي تضمن سلامة الذاكرة في مرحلة الترجمة، مما يمنع سباقات البيانات. في هذه المقالة، سنشرح كيفية العمل مع الخيوط، القنوات، الميوتكسات، والبرمجة غير المتزامنة في Rust.
1. أساسيات تعدد الخيوط: إنشاء وإدارة الخيوط
في Rust، يتم إنشاء الخيوط من خلال المكتبة القياسية std::thread. كل خيط يتم تنفيذه بشكل مستقل، لكن المترجم يراقب بدقة عمر المتغيرات.
إنشاء خيط
use std::thread;use std::time::Duration;
fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!("الخيط: {}", i); thread::sleep(Duration::from_millis(100)); } });
for i in 1..5 { println!("الخيط الرئيسي: {}", i); thread::sleep(Duration::from_millis(100)); }
handle.join().unwrap(); // ننتظر انتهاء الخيط}الاستيلاء على الملكية في الإغلاقات
إذا كان الخيط يستخدم متغيرات من النطاق الخارجي، يجب نقل الملكية عبر move:
let data = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("البيانات: {:?}", data);});
handle.join().unwrap();// println!("{:?}", data); // خطأ! تم نقل data2. تبادل البيانات: الميوتكسات والأقفال
للوصول الآمن إلى البيانات المشتركة من عدة خيوط، تستخدم Rust Mutex (الميوتكس) مع Arc (عداد المرجع الذري).
مثال مع Mutex و Arc
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!("النتيجة: {}", *counter.lock().unwrap()); // 10}مهم: Mutex يتم فتحه تلقائياً عند الخروج من النطاق. هذا يمنع deadlock (الحظر المتبادل) على مستوى المترجم.
RwLock — للقراءة المتكررة
إذا كانت البيانات نادراً ما تتغير ولكن تُقرأ كثيراً، استخدم RwLock:
use std::sync::RwLock;
let lock = RwLock::new(5);
{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); // يمكن القراءة بشكل متزامن println!("{} {}", r1, r2);}
{ let mut w = lock.write().unwrap(); *w += 1; // الكتابة تحظر القراءة}3. القنوات: نقل الرسائل بين الخيوط
تدعم Rust نموذج الممثلين عبر القنوات (std::sync::mpsc). هذا يسمح للخيوط بتبادل البيانات دون ذاكرة مشتركة.
مثال مع قناة
use std::sync::mpsc;use std::thread;
fn main() { let (tx, rx) = mpsc::channel();
thread::spawn(move || { let vals = vec!["hello", "from", "the", "thread"]; for val in vals { tx.send(val).unwrap(); thread::sleep(std::time::Duration::from_secs(1)); } });
for received in rx { println!("تم الاستلام: {}", received); }}مرسلون متعددون
باستخدام mpsc::Sender يمكن استنساخ المرسل لعدة خيوط:
let (tx, rx) = mpsc::channel();let tx1 = tx.clone();
thread::spawn(move || { tx1.send("الخيط 1").unwrap(); });thread::spawn(move || { tx.send("الخيط 2").unwrap(); });
```