Goで学ぶContext制御
コンテキストの基本
Go実践において、非同期処理やリクエストのスコープを管理するために不可欠な仕組みが context です。Context はキャンセル信号やデッドライン、値の伝播を一元化し、複数のゴルーチン間で安全に共有できます。最も基本的なコンテキストは context.Background() で作成され、トップレベルのエントリポイントで使用されます。
ctx := context.Background()
この ctx を引数に渡すことで、呼び出し側は処理のキャンセルやタイムアウトを制御できるようになります。Context は不変であるため、複数のゴルーチンで同時に安全に利用できます。
タイムアウトとキャンセル
実際のアプリケーションでは、外部サービスへのリクエストや長時間実行される計算に対してタイムアウトを設定することが重要です。context.WithTimeout を使うと、指定した時間が経過した時点で自動的にキャンセルされます。
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
また、手動でキャンセルしたい場合は context.WithCancel を使用します。cancel() を呼び出すと、関連するすべてのゴルーチンにキャンセル信号が送られます。
ctx, cancel := context.WithCancel(context.Background())
go func() {
// 長時間実行される処理
select {
case <-ctx.Done():
// キャンセルされたらここで終了
}
}()
time.Sleep(2 * time.Second)
cancel() // ここでキャンセル
キャンセルやタイムアウトは ctx.Done() チャネルで検知でき、エラーとして ctx.Err() を取得します。これにより、呼び出し側は失敗理由を明確に把握できます。
値の伝播と制御
コンテキストは値を持つこともでき、context.WithValue でキーと値を設定します。これにより、リクエスト固有の情報(ユーザーIDやトレーシングIDなど)をゴルーチン間で安全に共有できます。
type key int
const userIDKey key = 0
ctx = context.WithValue(ctx, userIDKey, 12345)
値は ctx.Value(key) で取得できますが、キーは型安全にするために独自の型を使うことが推奨されます。値の伝播は読み取り専用であり、変更はできません。これにより、データ競合を防ぎます。
まとめると、context はタイムアウト・キャンセル・値の伝播を一元化し、Go実践における非同期処理の制御をシンプルにします。WithCancel と WithTimeout を組み合わせることで、柔軟かつ安全な制御フローを構築できます。
コメント
コメントを投稿