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

Go / المتوسط: الحزم والعمل المتزامن / الدرس 10

السياق والمواعيد والإلغاء

مرر عمر العملية عبر الاستدعاءات وميز طلب الإلغاء من انتظار توقف العمل.

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

أهداف الدرس

  • تمرير Context صراحة لعمليات الطلب.
  • استدعاء دوال الإلغاء وفحص أسبابه.
  • تصميم عمل حاجب يستطيع ملاحظة الإنهاء.

يحمل السياق عمر العملية

تحمل context.Context الإلغاء والمواعيد وقيم الطلب عبر الواجهات. تكون اصطلاحا المعامل الأول باسم ctx وتمرر صراحة بدلا من تخزين دائم في بنية. لا تمرر nil واختر أصلا مناسبا. يشتق الابن عمرا أضيق ولا يمدد موعدا فرضه الأصل.

السياق آمن للاستخدام المتزامن لكن قيمه الداخلية لا تصبح حالة مشتركة قابلة للتعديل آمنة تلقائيا. أبق معاملات العمل العادية مستقلة. استخدم قيم السياق لمعلومات الطلب العابرة للحدود بمفاتيح خاصة الأنواع تمنع التصادم.

اطلب التوقف ثم انتظر الاكتمال

تعيد WithCancel ابنا ودالة إلغاء. يغلق cancel قناة Done في النهاية ويحرر الموارد المرتبطة، وتكراره آمن. لا ينتظر انتهاء الروتينات؛ لذا تلزم قناة اكتمال أو WaitGroup قبل تحرير حالة قد يستخدمها العامل.

يبدأ المثال الأول عاملا ينتظر الإلغاء ثم يلغي وينتظر done. يغلق العامل الإشارة عند العودة. لا يعتمد ذلك على النوم أو توقيت الجدولة. ويجب أن يضع العامل الحقيقي فحوص الإلغاء حول كل مرحلة حاجبة لا مرة عند البداية.

go إلغاء مع اكتمال صريح
package main

import (
	"context"
	"fmt"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	done := make(chan struct{})
	go func() { defer close(done); <-ctx.Done() }()
	cancel()
	<-done
	fmt.Println("worker stopped", ctx.Err() == context.Canceled)
}
الناتج المتوقع
worker stopped true

تلاحظ main اكتمال العامل بدلا من تخمين مدة توقفه.

الموعد حد وليس تعليمة نوم

تشتق WithDeadline وWithTimeout الإلغاء من الوقت. استدع دالة الإلغاء عند انتهاء العملية حتى إن كان الموعد سينقضي لاحقا؛ فهذا يحرر الموارد مبكرا ويوضح الملكية. لا يضمن الموعد توقف شيفرة عشوائية؛ يجب أن تلاحظه الواجهة أو العامل أو تستخدم آلية إلغاء محدودة أخرى.

يقدم المثال التالي موعدا ماضيا بالفعل فيفحص الخطأ دون انتظار أو نشر وقت غير ثابت. تميز Err بين context.Canceled وcontext.DeadlineExceeded. ويمكن تغليف الخطأ بسياق مع حفظ errors.Is إذا كان ذلك من العقد.

go فحص موعد انقضى بالفعل
package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
	defer cancel()
	fmt.Println("expired", ctx.Err() == context.DeadlineExceeded)
}
الناتج المتوقع
expired true

ينتج الموعد الماضي DeadlineExceeded دون نوم أو قياس توقيت.

جعل كل مرحلة حاجبة قابلة للإلغاء

تنتظر select قناة ctx.Done بجانب إرسال أو استقبال. لكنها لا تقاطع عملية بدأت خارجها. استخدم واجهات واعية بالسياق أو مواعيد أو إغلاقا ببروتوكول ملكية أو عمليات محدودة. قد ينقل إطلاق روتين حول كل استدعاء غير قابل للإلغاء التسرب للخلفية فقط.

إذا جهزت عدة حالات select فلا تعطي الإلغاء أولوية مطلقة. يجب تحمل الاختيار المسموح أو فحص الحدود صراحة. قد يعالج عامل عنصرا بالتزامن مع الإلغاء؛ فحدد جوازه وموعد اعتماد الآثار الخارجية.

حفظ السبب عند الحد الصحيح

مرر السياق الوارد ولا تستبدله بـBackground داخل الطلب؛ فالجذر الجديد يسقط موعد المستدعي وإلغاءه. إذا وجب تنظيف يتجاوز الطلب فصمم له عمرا مستقلا محدودا عمدا لا فصلا عرضيا. تتوافر أسباب الإلغاء في واجهات توافق الأساس عند الحاجة لسبب أغنى.

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

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

يواصل طلب ملغى عملا تابعا.

لماذا يحدث؟
استبدلت دالة داخلية الأصل بـBackground.
كيف تصلحه؟
مرر السياق الوارد عبر الاستدعاءات.
كيف تتجنب تكراره؟
اختبر الانتشار من العملية الخارجية.

يعود cancel والعامل يصل لحالة محررة.

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

يتجاهل عامل الإلغاء أثناء إرسال قناة.

لماذا يحدث؟
لا يملك الإرسال الحاجب بديل إلغاء.
كيف تصلحه؟
استخدم select مع Done وحدد ملكية غير المرسل.
كيف تتجنب تكراره؟
راجع كل عملية قد تحجب.

اختبر فهمك

1. تستدعي دالة WithTimeout وتعود مبكرا بالنجاح. لماذا تستدعي cancel؟

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

قد يحتفظ السياق بالمؤقت وعلاقته بالأصل حتى الإلغاء أو الانقضاء. يحرر cancel الموارد سريعا ويبين ملكية العمر. استخدم defer cancel بعد الاشتقاق لعملية ضمن الدالة. ولا يغني ذلك عن انتظار روتين أنشأته الدالة وتملكه.

2. عامل يملك في select إرسال نتيجة جاهزا وDone ملغاة. هل قد يرسل؟ صمم عقدا متينا.

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

نعم قد تختار select أيا من الجاهزين. اسمح بنتيجة أخيرة متسابقة ويتجاهلها المستقبل بعد الإلغاء، أو صمم اعتمادا أقوى إذا لزم. لا تدع أولوية من ترتيب المصدر. وفي الحالين ضمن عودة العامل وملاحظة اكتماله.

أهم ما تعلمته

  • إلغاء السياق تعاوني ولا ينضم للعاملين.
  • مرر عمر الأصل وألغ السياقات المشتقة سريعا.
  • تحتاج كل مرحلة حاجبة وأثر خارجي سياسة إلغاء مقصودة.

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