דילוג לתוכן המרכזי
מודול 7 · שיעור 5 מתוך 535/50 בקורס~14 min
שיעורי מודול (5/5)

`context.Context`: ביטול ומועדים

החבילה context היא התקן דה-פקטו לריבוי ביטול, מועדים וערכים מוגבלים להיקף לאורך שרשרת השיחות, על פני מספר גורוטינים. כל חבילות ה"רשתות" הסטנדרטיות (net/http, database/sql, os/exec) קבל context.Context בתור הפרמטר הראשון שלהם.

הממשק

Go
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(): חיפוש ערכים מוגבלים להיקף (השתמש במשורה).

ארבע הפונקציות הבסיסיות

Go
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) כדי לשחרר משאבים פנימיים, גם אם ההקשר כבר פג.

דפוס פסק זמן

Go
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 לוקח יותר מ-2 שניות, ctx.Done() סגור ו הפונקציה חייבת לבטל את הפעולה.

כיבוד ההקשר: בחר ב- Done()

Go
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(). אחרת ההקשר "מופעל" עבור כלום.

מוסכמה של פרמטר ראשון

Go
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
//        ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionale
  • ctx תמיד כפרמטר הראשון, בשם ctx.
  • לעולם לא לשים את Context כשדה של מבנה (חריגים נדירים מאוד).
  • לעולם לא לעבור את nil: השתמש ב-context.TODO() בתור בדל.

context.WithValue: במשורה

Go
type userKey struct{}

ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }

הנחיות:

  • רק עבור נתונים בהיקף של בקשה (מזהה בקשה, מזהה מעקב, זהות משתמש).
  • לעולם לא עבור פרמטרים של פונקציות: העבר אותם כרגיל.
  • מפתחות מסוג מותאם אישית לא מיוצא** כדי למנוע התנגשויות.

ריבוי לאורך השרשרת

Go
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() כאשר הלקוח מתנתק או כאשר המטפל חוזר. על ידי הפצתו לכולם שיחות במורד הזרם, כל עץ הפעולות מתבטל מפל: אין עבודה מבוזבזת לאחר ניתוק.

נסה את זה

פעילות גופנית#go.m7.l5.e1
ניסיונות: 0טוען...

צור הקשר עם פסק זמן של שנייה אחת שנגזר מ-context.Background() וזכור להתקשר לביטול עם defer.

טוען עורך...
הצג רמז

`context.WithTimeout(parent, d)` מחזיר `(ctx, cancel)`; תמיד להתקשר לבטל.

הפתרון זמין לאחר 3 ניסיונות

פעילות גופנית#go.m7.l5.e2
ניסיונות: 0טוען...

השתמש ב-<-ctx.Done() בבחירה כדי להמתין לביטול ההקשר.

טוען עורך...
הצג רמז

`ctx.Done()` הוא ערוץ שנסגר כאשר ההקשר מתבטל.

הפתרון זמין לאחר 3 ניסיונות

Quiz#go.m7.l5.e3
מוכן

לפי המוסכמה, לאן עובר הפרמטר ctx בחתימת פונקציה?

Go
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() בוטל בעת ניתוק הלקוח.