اختبار التزامن
Concurrency Quiz
اختبار التزامن — Concurrency Quiz
التزامن في Go يصبح واضحاً عندما تعرف من يرسل، من يستقبل، ومن يغلق القناة. هذه أسئلة عملية صغيرة.
هذا الاختبار يراجع الفهم التشغيلي لا الكلمات. في التزامن، الخطأ غالباً لا يكون في syntax بل في ترتيب المسؤوليات: goroutine بدأت ولم ينتظرها أحد، channel لم تغلق، أو بيانات مشتركة زادت بدون حماية. لذلك اقرأ كل سؤال كأنه رسم صغير لتدفق العمل. من يملك القناة؟ من يكتب؟ من يقرأ؟ متى ينتهي البرنامج؟
هذا المثال يذكرك بالشكل الآمن البسيط: goroutine ترسل، ثم تغلق، وmain يقرأ حتى تنتهي القناة.
لا تحفظ المثال كقالب جامد، بل لاحظ العقد: اتجاه القناة في توقيع الدالة يقول إن الدالة ترسل فقط، وdefer close(out) يضمن أن القارئ لن ينتظر للأبد. هذه التفاصيل الصغيرة هي التي تجعل كود التزامن قابلاً للفهم.
السؤال 1: قناة باتجاه واحد
اكتب دالة ترسل الأرقام من 1 إلى 3 ثم تغلق القناة.
بعد حل السؤال الأول، تأكد أنك أغلقت القناة داخل sendNumbers لا داخل main. المرسل هو من يعرف أنه انتهى من الإرسال. إذا لم تغلق القناة، ستبقى حلقة range في main تنتظر قيمة رابعة لن تأتي.
السؤال 2: select مع timeout
اختر الاستجابة إذا وصلت بسرعة، وإلا اطبع timeout.
في السؤال الثاني، القناة done لا ترسل شيئاً، لذلك المسار الوحيد الذي يجب أن يكتمل هو time.After. هذا النمط يظهر في الطلبات الخارجية: انتظر نتيجة، لكن لا تنتظر للأبد. المهم أن تجعل المدة قصيرة في التحديات حتى يكون التشغيل سريعاً، وواضحة في الإنتاج حتى تعكس احتياج النظام.
السؤال 3: عداد آمن
أكمل الكود ليزيد العداد 100 مرة بأمان.
في السؤال الثالث لا يكفي أن تضيف counter++ داخل goroutine. العملية تبدو سطراً واحداً لكنها قراءة ثم زيادة ثم كتابة. عندما تعمل 100 goroutine معاً قد تتداخل هذه الخطوات وتضيع زيادات. Mutex يجعل جزءاً صغيراً من الكود يعمل كمنطقة محمية، وWaitGroup يضمن أن الطباعة لا تحدث قبل انتهاء الزيادات.
طريقة التفكير في الإجابات
ابدأ دائماً من نهاية البرنامج. كيف يعرف main أن العمل انتهى؟ في السؤال الأول يعرف لأن القناة أغلقت. في السؤال الثاني يعرف لأن select اختار timeout. في السؤال الثالث يعرف لأن wg.Wait() رجع بعد انتهاء كل goroutines. إذا لم تجد جواباً واضحاً لهذا السؤال، فغالباً يوجد احتمال تعليق أو نتيجة غير مكتملة.
لا تعتمد على النوم العشوائي مثل time.Sleep لإصلاح التزامن. النوم يخفي المشكلة ولا يثبت أن العمل انتهى. استخدم الأدوات التي تعبّر عن النية: channel للتواصل، WaitGroup للانتظار، Mutex لحماية الذاكرة المشتركة، وselect للتعامل مع أكثر من احتمال.
مراجعة سريعة
goيبدأ عملاً متزامناً.channelيربط goroutines بإرسال واستقبال.selectينتظر أكثر من احتمال.Mutexيحمي البيانات المشتركة عندما لا يكون channel هو الأنسب.
إذا نجحت في هذه الأسئلة، فأنت لا تعرف فقط كيف تكتب go func(). أنت بدأت تفهم عقود التزامن: الملكية، الإنهاء، والحماية. هذه العقود أهم من عدد الأسطر، لأنها تمنع الأخطاء الصعبة التي لا تظهر دائماً في أول تشغيل.