ProgrammingバックエンドGo開発者

Goの組み込みパッケージcontextはどのように機能し、何に使用され、どのように正しくコンテキストを作成および終了させるべきか?使用時の間違いは何か?

Hintsage AIアシスタントで面接を突破

回答

contextパッケージは、特にgoroutineや外部呼び出し(HTTP、DB)でコードの部分間でメタデータを渡すためのライフサイクル管理(キャンセル、タイムアウト)の標準です。

作成方法:

  • context.Background() — ルートコンテキスト
  • context.WithCancel(parent) — キャンセル可能な新しいコンテキスト
  • context.WithTimeout(parent, duration) — タイムアウト付き
  • context.WithValue(parent, key, value) — データを含む

リソースを正しく解放するためにコンテキストを終了させることが重要です。例:

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // その後、ctxはgoroutine/HTTPリクエストで使用される

典型的なパターンは、関数の最初の引数としてcontextを渡すことです。例:

func process(ctx context.Context) error { ... }

騙しの質問

context.TODO()やcontext.Background()を実際のcontextの代わりに渡すことができるのはいつで、2つの違いは何ですか?

多くの人がcontext.Background()をプレースホルダーとして使用します。しかし:

  • context.Background()はツリーのルートであり、main()やテストでのみ使用し、コンテキストの「ツリー」を初期化する際にのみ使用すべきです。
  • context.TODO()は、どのようなコンテキストが必要かがまだ不明な場合に必要です。リファクタリング時の一時的な「穴埋め」として使用されます。本番コードではTODOは受け入れられません。何がどこに渡されているかを正確に把握する必要があります。

このテーマの詳細を知らないことから生じる実際の誤りの例


物語

マイクロサービスでcontext.WithTimeout()作成後にcancel()を呼び出すのを忘れたため、リクエストが停止し、完了したgoroutineがランタイムに「ぶら下がった」タイマーを残し、メモリリークを引き起こしました。


物語

ハンドラーでリクエストから来たcontextの代わりにcontext.Background()を渡したため、キャンセルチェーンとtrace-idが失われ、タイムアウトの制限が機能せず、リクエストのキャンセルが発動しませんでした。


物語

context.WithValue()を介して開発者がデータを渡しましたが、キーが文字列でした。その結果、異なるパッケージからのキーの衝突により予期しないエラー(「キーはすでに占有されています」)が発生しました。正しいプラクティスは、キーのためにユニークな型を使用することです。