モジュールのレッスン (5/5)
`context.Context`: キャンセルと期限
context パッケージは 伝播の事実上の標準です
呼び出しチェーンに沿ったキャンセル、期限、スコープ限定の値、
複数のゴルーチンにわたって。すべての標準「ネットワーク」パッケージ
(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(): スコープ限定の値の検索 (慎重に使用してください)。
4つの基本機能
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 に 2 秒以上かかる場合、ctx.Done() は閉じられ、
関数は操作を中止する必要があります。
コンテキストを尊重: 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 { ... }ガイドライン:
- リクエスト スコープ データ (リクエスト ID、トレース ID、ユーザー ID) のみ。
- 関数パラメータには決して渡さないでください。通常通り渡します。
- 衝突を避けるための カスタムの非エクスポート タイプのキー。
チェーンに沿った伝播
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() から派生した 1 秒のタイムアウトを持つコンテキストを作成し、defer を指定して cancel を呼び出すことを忘れないでください。
ヒントを表示
`context.WithTimeout(parent, d)` returns `(ctx, cancel)`;常にキャンセルを呼び出します。
3 回の試行後に解決策が利用可能になります
選択で <-ctx.Done() を使用して、コンテキストがキャンセルされるのを待ちます。
ヒントを表示
`ctx.Done()` はコンテキストがキャンセルされたときに閉じるチャネルです。
3 回の試行後に解決策が利用可能になります
慣例により、ctx パラメーターは関数シグネチャのどこに置かれますか?
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()がキャンセルされます。