محتويات الدورة

Rust / المتقدم / الدرس 15

المستقبلات غير المتزامنة وPin

افصل المستقبل عن منفذه وافهم التعليق والإلغاء والتثبيت دون بناء آليات ذاتية المرجع غير آمنة.

وقت القراءة التقريبي: 4 دقيقةتاريخ مراجعة المحتوى
في هذه الصفحة

أهداف الدرس

  • شرح Future::poll وPending وReady والإيقاظ.
  • وصف Pin وUnpin عند حد الكائن المشار إليه.
  • فصل المقاطع المحتاجة بيئة تنفيذ عن الأمثلة القياسية.

المستقبل قيمة تصف عملًا غير مكتمل

تعيد الدالة async مستقبلًا ولا تنشئ خيطًا تلقائيًا. يبني استدعاؤها قيمة يتقدم جسمها عندما يجسها منفذ. تتعاون await مع الجس ولا تضمن عدم حجب الخيط لكل عملية. تعرّف المكتبة Future وأدوات المهام ولا توفر بيئة تطبيق عامة بالشبكات والمؤقتات. اختر البيئة وفق الإدخال والجدولة ووثّق اعتمادها. تستخدم الأمثلة مستقبلات جاهزة فورًا فقط، دون تثبيت بيئة خارجية أو افتراضها.

الجس مرة عند ضمان الجاهزية

تستقبل Future::poll مرجعًا قابلًا للتعديل مثبتًا وContext بها Waker. تعيد Ready الإكمال وتعني Pending الحاجة إلى تقدم لاحق ببروتوكول إيقاظ. لا ينبغي لمنفذ فعلي الدوران على Pending بلا احترام الإيقاظ. يستخدم المثال ready التي تكتمل بهذا الجس، وتطبيق Wake بلا عمل لبناء السياق فقط. هذا عرض مضبوط لا منفذ عام. لا تجس مستقبلًا مكتملًا مجددًا إلا بدعم صريح من واجهته.

rust جس واحد لمستقبل جاهز فورًا
use std::future::{Future, ready};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};
struct Noop;
impl Wake for Noop { fn wake(self: Arc<Self>) {} }
fn main() {
    let waker = Waker::from(Arc::new(Noop));
    let mut context = Context::from_waker(&waker);
    let mut future = ready(7u32);
    match Pin::new(&mut future).poll(&mut context) {
        Poll::Ready(value) => println!("ready {value}"),
        Poll::Pending => println!("pending"),
    }
}
الناتج المتوقع
ready 7

تضمن ready الإكمال هنا، ولا يناسب الموقظ الخامل إلا غياب الحاجة لحدث جاهزية لاحق.

تقيد Pin نقل الكائن المشار إليه

قد تحمل بعض المستقبلات مراجع داخلية عبر التعليق فتحتاج عنوانًا ثابتًا بعد التثبيت. تقيد Pin<Ptr> نقل الكائن أو إبطاله، لا نقل مقبض المؤشر نفسه. تعني Unpin عدم حاجة النوع لهذه القيود في استخدامه المعتاد. أنواع كثيرة Unpin ولا يفترض ذلك لمستقبل async. توفر Box::pin تخزينًا مثبتًا مملوكًا بأمان. لا تصحح Pin مؤشرات unsafe اعتباطية ولا تجمد الحقول أو تمدد الأعمار؛ ويلزم حفظ العقد أثناء إسقاط الحقول وإتلافها.

تركيب عمل جاهز دون بيئة خارجية

يثبت المثال كتلة async تنتظر قيمة جاهزة وتضيف خمسة. يكتمل المستقبل بجس واحد لأن await المحددة لا تعود Pending. يستقل الناتج عن المجدول والمؤقت والإدخال الخارجي. ينتظر المستدعي عادة بدل بناء Context يدويًا. يحتاج spawn أو مؤقت خاص ببيئة إلى حزمته وإعدادها ولا يوسم مستقلًا. افصل في التوثيق صياغة اللغة والمستقبلات القياسية وخدمات المنفذ.

rust تثبيت آمن لحساب async محدد
use std::future::{Future, ready};
use std::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};
struct Noop;
impl Wake for Noop { fn wake(self: Arc<Self>) {} }
fn main() {
    let waker = Waker::from(Arc::new(Noop));
    let mut context = Context::from_waker(&waker);
    let mut future = Box::pin(async { ready(4u32).await + 5 });
    match future.as_mut().poll(&mut context) {
        Poll::Ready(value) => println!("sum={value}"),
        Poll::Pending => println!("pending"),
    }
}
الناتج المتوقع
sum=9

الأربعة جاهزة فورًا فيعيد الحساب المثبت تسعة دون تعليق.

يبقي التعليق التزامات الملكية

يحتفظ المستقبل بالقيم والاستعارات عبر التعليق. يلغي إسقاطه قيمته ولا يتراجع تلقائيًا عن آثار خارجية أو يلغي مهام منشأة مستقلًا. صمّم انتقالات آمنة للإلغاء وحدّد الالتزام قبل await. قد يحجب حارس قفل متزامن عبر التعليق مهام أخرى أو يمنع Send؛ فقصّر المقطع أو اتبع بروتوكول async موثقًا. قد يتطلب منفذ متعدد الخيوط Send ويسمح المحلي بغيره؛ وهذه عقود منفذ لا خواص عامة للصياغة.

الأخطاء الشائعة وإصلاحها

افتراض بدء عمل خلفي بمجرد استدعاء async.

لماذا يحدث؟
خُلط إنشاء المستقبل بجسه أو إطلاقه.
كيف تصلحه؟
انتظر أو أطلق عبر المنفذ الموثق المقصود.
كيف تتجنب تكراره؟
حدّد المكون الذي يدفع كل مستقبل.

استخدام موقظ خامل لإدخال حقيقي.

لماذا يحدث؟
اعتُبر عرض الجاهزية منفذًا عامًا.
كيف تصلحه؟
استخدم بيئة ببروتوكول جاهزية وإيقاظ صحيح.
كيف تتجنب تكراره؟
ميّز أمثلة المنفذ التابع صراحة.

افتراض أن Pin تجعل كل حقل ثابتًا.

لماذا يحدث؟
خُلط ثبات العنوان بالثبات العام.
كيف تصلحه؟
راجع النقل وUnpin والإسقاط إلى الحقول منفصلة.
كيف تتجنب تكراره؟
لا تضع تطبيقات التثبيت unsafe في تعديلات عابرة.

اختبر فهمك

1. استبدل ready(4u32) بـready(10u32). توقع الناتج وسبب كفاية الموقظ الخامل لهذه الحالة تحديدًا.

اعرض الحل المشروح

يصبح السطر sum=15. تعيد ready القيمة في الجس الأول فتكتمل الكتلة دون حدث خارجي أو جس مجدول لاحق. يعتمد ذلك على الحساب المحدد؛ واستبداله بمؤقت أو مستقبل مقبس يبطل الفرض ويحتاج منفذًا حقيقيًا.

2. تحدّث مهمة سجلًا ثم تنتظر الشبكة ثم تكمل التحديث. صف خطر الإلغاء وعقدًا أفضل.

اعرض الحل المشروح

قد يترك إسقاط المستقبل عند await التعديل الأول دون الثاني. حضّر مرشحًا قبل التغيير الظاهر أو حدّد التزامًا مقصودًا أو حالة وسطية قابلة للتعويض. ليس الإلغاء تراجع معاملة تلقائيًا. اختبر الإسقاط عند التعليق وحدّد استمرار العمل المنشأ مستقلًا.

أهم ما تعلمته

  • تحتاج المستقبلات منفذًا أو عرض جس مبررًا.
  • تحمي Pin عقد نقل ولا تصلح unsafe اعتباطية.
  • راجع الإلغاء والحراس والالتقاط عبر await.

المراجع الرسمية