دروس الوحدة (5/5)
`السياق.السياق`: الإلغاء والمواعيد النهائية
الحزمة context هي المعيار الفعلي للنشر **
الإلغاء والمواعيد النهائية والقيم المرتبطة بالنطاق ** على طول سلسلة الاتصال،
عبر goroutines متعددة. جميع حزم "الشبكات" القياسية
(net/http، database/sql، os/exec) قبول context.Context كـ
المعلمة الأولى لهم
الواجهة
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}Done(): القناة التي يتم إغلاقها عند إلغاء السياق.Err(): سبب الإلغاء (context.Canceledأوcontext.DeadlineExceeded).Deadline(): موعد نهائي اختياري.Value(): بحث عن القيم المرتبطة بالنطاق (استخدمه باعتدال).
الوظائف الأربع الأساسية
ctx := context.Background() // radice: usalo in main, init, test
ctx := context.TODO() // "non so ancora": stub esplicito
ctx, cancel := context.WithCancel(parent) // cancel manuale
ctx, cancel := context.WithTimeout(parent, d) // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t) // scadenza assoluta
ctx := context.WithValue(parent, key, value) // valore scope-boundتُرجع كافة النماذج المشتقة دالة cancel: اتصل بها دائمًا
(مثاليًا مع defer) لتحرير الموارد الداخلية، حتى لو كان
لقد انتهت صلاحية السياق بالفعل.
نمط المهلة
func fetchUser(id string) (*User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
return queryUser(ctx, id) // queryUser deve rispettare ctx
}إذا استغرق queryUser أكثر من ثانيتين، فسيتم إغلاق ctx.Done() و
يجب أن تقوم الوظيفة بإحباط العملية.
احترام السياق: حدد على تم ()
func slowWork(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err() // canceled o deadline exceeded
case <-time.After(100 * time.Millisecond):
// un tick di lavoro
}
}
}القاعدة: في أي مكان قد يتم حظر وظيفتك فيه لفترة طويلة، فإنه ** يجب التعامل مع ctx.Done()**. وإلا فسيتم "تشغيل" السياق لـ لا شيء.
اتفاقية المعلمة الأولى
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
// ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionalectxدائمًا كمعلمة أولى، تسمىctx.- لا تضع أبدًا
Contextكحقل للبنية (استثناءات نادرة جدًا). - لا تمر أبدًا
nil: استخدمcontext.TODO()ككعب روتين.
context.WithValue: باعتدال
type userKey struct{}
ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }المبادئ التوجيهية:
- فقط للبيانات ** على مستوى الطلب ** (معرف الطلب، ومعرف التتبع، وهوية المستخدم).
- أبدًا بالنسبة لمعلمات الوظيفة: قم بتمريرها بشكل طبيعي.
- مفاتيح من النوع ** المخصص غير المُصدَّر ** لتجنب الاصطدامات.
الانتشار على طول السلسلة
func handleHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // context legato al ciclo di vita della richiesta
user, err := fetchUser(ctx, r.URL.Query().Get("id"))
if err != nil { ... }
// ...
}net/http يلغي تلقائيًا r.Context() عندما يقوم العميل
قطع الاتصال أو عندما يعود المعالج. من خلال نشرها للجميع
المكالمات النهائية، يتم إلغاء شجرة العمليات بأكملها
تتالي: لا يوجد عمل ضائع بعد قطع الاتصال.
جربه
قم بإنشاء سياق بمهلة ثانية واحدة مشتقة من context.Background() وتذكر استدعاء الإلغاء مع التأجيل.
إظهار التلميح
`context.WithTimeout(parent, d)` يُرجع `(ctx, Cancel)`; اتصل دائمًا بالإلغاء.
الحل متاح بعد 3 من المحاولات
استخدم <-ctx.Done() في التحديد لانتظار إلغاء السياق.
إظهار التلميح
`ctx.Done()` هي قناة تُغلق عند إلغاء السياق.
الحل متاح بعد 3 من المحاولات
حسب الاصطلاح، أين تذهب معلمة ctx في توقيع الوظيفة؟
func Fetch(???, url string) ([]byte, error)خلاصة
context.Contextينشر الإلغاء والموعد النهائي والقيم المرتبطة بالنطاق.Background()/TODO()كجذر؛WithCancel/WithTimeout/WithDeadlineللاشتقاق.defer cancel()دائمًا لتحرير الموارد.- يجب أن تكون الوظائف "الطويلة"
selectعلىctx.Done()أو تمرر ctx إلى الحزم القياسية. - الاصطلاح:
ctxكمعلمة FIRST، وليس داخل البنية أبدًا. WithValueفقط للبيانات ذات نطاق الطلب (معرف الطلب، معرف التتبع).- في
net/http، يتم إلغاءr.Context()عند قطع اتصال العميل.