قنوات Crossbeam
قنوات متقدمة
قنوات Crossbeam درس مجاني في Learn Rust Coding على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Learn Rust Coding، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
ما بعد std mpsc
قناة mpsc القياسية هي أحادية المستهلك، أي لا تحتوي إلا على مستقبِل واحد. وتوفر حزمة crossbeam-channel قنوات متعددة المنتجين ومتعددة المستهلكين (mpmc)، مع مزايا أكثر وأداء أفضل غالبًا.
أبرز المزايا:
- إمكانية استنساخ
Receiver، وليس المرسِلات فقط. - وحدة ماكرو قوية
select!للانتظار على قنوات متعددة. - قنوات محدودة وغير محدودة وقنوات خاصة مثل قنوات النبضات.
إضافة الاعتمادية
Crossbeam حزمة خارجية، لذا أضفها إلى Cargo.toml:
[dependencies]crossbeam-channel = "0.5"
ثم استورد دوالها. وبما أن هذا يتطلب Cargo وحزمة خارجية، فإن المقاطع هنا توضّح واجهة البرمجة بدلًا من أن تعمل بصورة مستقلة.
// Cargo.toml
// [dependencies]
// crossbeam-channel = "0.5"
use crossbeam_channel::unbounded;
fn main() {
let (s, r) = unbounded();
s.send("hello").unwrap();
println!("{}", r.recv().unwrap());
}unbounded و bounded
توفر Crossbeam مُنشئَين رئيسيين:
- تنمو
unbounded()حسب الحاجة؛ ولا يحظرsendالتنفيذ أبدًا. - تملك
bounded(cap)مخزنًا مؤقتًا ثابت الحجم؛ ويحظرsendالتنفيذ عندما يمتلئ، مما يوفر آلية التحكم في التدفق العكسي.
قناة bounded(0) هي قناة لقاء مباشر، إذ يسلّم كل من send و recv البيانات مباشرةً.
use crossbeam_channel::bounded;
use std::thread;
fn main() {
let (s, r) = bounded(2);
thread::spawn(move || {
for i in 1..=3 {
s.send(i).unwrap();
}
});
while let Ok(v) = r.recv() {
println!("got {}", v);
}
}مستهلكون متعددون
الميزة الأساسية هي إمكانية استنساخ Receiver. إذ يمكن لعدة خيوط عاملة سحب الرسائل من القناة نفسها، وتذهب كل رسالة إلى واحد منها بالضبط. وهذا هو الأساس الذي تُبنى عليه مجموعة خيوط لسرقة العمل.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (s, r) = unbounded();
let mut workers = vec![];
for id in 0..3 {
let rx = r.clone();
workers.push(thread::spawn(move || {
while let Ok(job) = rx.recv() {
println!("worker {} got job {}", id, job);
}
}));
}
for job in 0..6 { s.send(job).unwrap(); }
drop(s);
for w in workers { w.join().unwrap(); }
}ماكرو select!
تتيح select! لخيط واحد الانتظار على عمليات قنوات متعددة وتنفيذ العملية الجاهزة أولًا. وهي تشبه match على أحداث القنوات، وتناسب الجمع بين المدخلات القادمة من مصادر متعددة.
use crossbeam_channel::{unbounded, select};
use std::thread;
fn main() {
let (s1, r1) = unbounded();
let (s2, r2) = unbounded();
thread::spawn(move || { s1.send("from one").unwrap(); });
thread::spawn(move || { s2.send("from two").unwrap(); });
for _ in 0..2 {
select! {
recv(r1) -> msg => println!("r1: {}", msg.unwrap()),
recv(r2) -> msg => println!("r2: {}", msg.unwrap()),
}
}
}المهلات الزمنية مع select!
تدعم select! ذراعًا باسم default وذراع مؤقت باسم recv(after(duration))، لذا يمكنك التوقف عن الانتظار بعد انتهاء المهلة بدلًا من الحظر إلى الأبد. وهذا ضروري للأنظمة سريعة الاستجابة.
use crossbeam_channel::{unbounded, select, after};
use std::time::Duration;
fn main() {
let (_s, r) = unbounded::<i32>();
select! {
recv(r) -> msg => println!("received {:?}", msg),
recv(after(Duration::from_millis(100))) -> _ => {
println!("timed out waiting for a message");
}
}
}try_send و try_recv
مثل المكتبة القياسية، توفر crossbeam إصدارات غير حاجزة. تفشل try_send فورًا إذا كانت القناة المحدودة ممتلئة، وتفشل try_recv فورًا إذا لم تكن هناك رسالة جاهزة. ويُعيد كلاهما خطأً وصفيًا يمكنك إجراء المطابقة عليه.
use crossbeam_channel::{bounded, TrySendError};
fn main() {
let (s, _r) = bounded(1);
s.send(1).unwrap();
match s.try_send(2) {
Ok(()) => println!("sent"),
Err(TrySendError::Full(v)) => println!("full, kept {}", v),
Err(TrySendError::Disconnected(v)) => println!("closed, kept {}", v),
}
}tick والعمل الدوري
تعيد الدالة tick(duration) مستقبِلًا يسلّم رسالة كل فترة زمنية ثابتة. ادمجها مع select! لتشغيل مهام دورية بالتوازي مع قنوات أخرى، مثل نبضة الحياة أو حلقة الاستقصاء.
use crossbeam_channel::{tick, select};
use std::time::Duration;
fn main() {
let ticker = tick(Duration::from_millis(50));
let mut count = 0;
while count < 3 {
select! {
recv(ticker) -> _ => {
count += 1;
println!("tick {}", count);
}
}
}
}خط أنابيب من المراحل
تتفوّق Crossbeam في بناء خطوط الأنابيب: تنتج المرحلة الأولى، وتحول المرحلة الثانية البيانات، وتستهلك المرحلة الثالثة النتائج. تعمل كل مرحلة في خيطها الخاص المتصل عبر القنوات، وتتيح لك المستقبلات المستنسخة توسيع أي مرحلة لتعمل عليها عدة خيوط عاملة.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (in_s, in_r) = unbounded();
let (out_s, out_r) = unbounded();
thread::spawn(move || {
for n in in_r { out_s.send(n * n).unwrap(); }
});
for n in 1..=4 { in_s.send(n).unwrap(); }
drop(in_s);
for sq in out_r { println!("square: {}", sq); }
}متى تتفوق Crossbeam على std
استخدم crossbeam-channel عندما تحتاج إلى:
- مستهلكين متعددين يتشاركون قائمة انتظار واحدة (مجموعات الخيوط العاملة).
select!على قنوات متعددة مع مهلات زمنية.- مؤقتات دورية عبر
tickمدمجة في عملية الاختيار.
أما في قناة بسيطة بين منتج واحد ومستهلك واحد، فتكفي mpsc القياسية ولا تحتاج إلى أي اعتمادية.
تُغلق القنوات بحذفها
مثل std تمامًا، تُغلق قنوات crossbeam عندما تُحذف جميع المرسِلات (من جهة المستقبِل) أو جميع المستقبلات (من جهة المرسِل). وينتهي التكرار على المستقبِل بعد حذف آخر مرسِل. احرص دائمًا على استخدام drop للمرسِلات أو وضعها ضمن نطاق، حتى تتمكن حلقات الخيوط العاملة من الخروج بطريقة نظيفة.
use crossbeam_channel::unbounded;
use std::thread;
fn main() {
let (s, r) = unbounded();
let h = thread::spawn(move || {
let total: i32 = r.iter().sum();
println!("total: {}", total);
});
for i in 1..=5 { s.send(i).unwrap(); }
drop(s); // closes channel so the loop ends
h.join().unwrap();
}اختبار سريع
اختبر مدى فهمك لقنوات crossbeam.
مراجعة
لقد استكشفت قنوات crossbeam:
- هي قنوات mpmc؛ إذ يمكن استنساخ كل من المرسِلات والمستقبلات.
- تتحكم
unbounded()وbounded(n)في التخزين المؤقت والتحكم في التدفق العكسي. - تنتظر
select!على قنوات متعددة، مع مهلات زمنية عبرafterوtickالدوري. - لا تحظر
try_send/try_recvالتنفيذ. - استخدم crossbeam لمجموعات الخيوط العاملة وخطوط الأنابيب المعقدة، وstd mpsc للقنوات البسيطة.
الأسئلة الشائعة
هل درس «قنوات Crossbeam» مجاني؟
نعم — نص درس «قنوات Crossbeam» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Learn Rust Coding، انتقل إلى CoddyKit PRO. تتضمن دورة Learn Rust Coding 4 دروس في المجموع.
ماذا ستتعلم في «قنوات Crossbeam»؟
قنوات متقدمة تتمرن على Learn Rust Coding مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Learn Rust Coding؟
لا تُشترط خبرة سابقة. Learn Rust Coding على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «قنوات Crossbeam»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Learn Rust Coding هذا؟
نعم. كل درس في Learn Rust Coding يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.