メインコンテンツにスキップ
モジュール 7 · レッスン 5 / 5コース内の 35/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(): スコープ限定の値の検索 (慎重に使用してください)。

4つの基本機能

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 { ... }

ガイドライン:

  • リクエスト スコープ データ (リクエスト ID、トレース ID、ユーザー ID) のみ。
  • 関数パラメータには決して渡さないでください。通常通り渡します。
  • 衝突を避けるための カスタムの非エクスポート タイプのキー。

チェーンに沿った伝播

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() から派生した 1 秒のタイムアウトを持つコンテキストを作成し、defer を指定して cancel を呼び出すことを忘れないでください。

エディターを読み込み中…
ヒントを表示

`context.WithTimeout(parent, d)` returns `(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() を使用します。
  • 「長い」関数は、ctx.Done() で select するか、標準パッケージに ctx を渡す必要があります。
  • 規則: ctx を FIRST パラメーターとして使用し、構造体内には使用しないでください。
  • WithValue はリクエスト範囲のデータ (リクエスト ID、トレース ID) のみです。
  • net/http では、クライアントの切断時に r.Context() がキャンセルされます。