【golang】contextの使い方

Go言語の標準パッケージである「context」は、複数のgoroutineが同時に実行されている場合に、それらの間で情報をやりとりするための手段となるパッケージです。

Contextの使い方

まず、contextを使用するためには、以下のようにパッケージをインポートします。

import "context"

Contextの作成

contextを作成するには、context.Background()を使用します。Background()は、空のcontextを返します。

ctx := context.Background()

また、キャンセル処理をする場合は、context.WithCancel()を使用します。WithCancel()は、キャンセル用のcancel関数を返します。このcancel関数を呼び出すことで、関連するgoroutineをキャンセルすることができます。

ctx, cancel := context.WithCancel(context.Background())

Contextの値の設定

Contextに値を設定する場合は、context.WithValue()を使用します。WithValue()は、新しいContextを返します。このContextには、キーと値のペアが追加されます。

ctx := context.WithValue(context.Background(), "key", "value")

Contextの取得

goroutineの中でContextを取得するには、context.Context型の変数を関数の引数に設定します。

func foo(ctx context.Context) {
  // ...
}

Contextのキャンセル

キャンセルをするには、cancel関数を呼び出します。

cancel()

Contextのタイムアウト

Contextには、タイムアウトを設定することもできます。Contextにタイムアウトを設定するには、context.WithTimeout()を使用します。WithTimeout()は、指定された時間が経過すると、キャンセル用のcancel関数を呼び出します。

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() // cancel関数は必ずdeferで実行すること

// Contextの処理

Contextのデッドライン

Contextには、デッドラインを設定することもできます。Contextにデッドラインを設定するには、context.WithDeadline()を使用します。WithDeadline()は、指定された時刻になると、キャンセル用のcancel関数を呼び出します。

deadline := time.Now().Add(time.Second * 10) // 10秒後の時刻を取得
ctx, cancel := context.WithDeadline(context.Background(), deadline)
defer cancel() // cancel関数は必ずdeferで実行すること

// Contextの処理

Contextのチェーン

Contextは、チェーンすることもできます。親Contextから子Contextを作成することで、子Contextには親Contextの値が引き継がれます。また、親Contextがキャンセルされた場合は、子Contextもキャンセルされます。

parentCtx := context.Background()
ctx := context.WithValue(parentCtx, "key", "value")

// 子Contextの作成
childCtx, cancel := context.WithCancel(ctx)
defer cancel()

// 親Contextの値を取得
value := ctx.Value("key")

// 子Contextの値を取得
childValue := childCtx.Value("key")

Contextの利用例

Contextは、HTTPリクエストのキャンセル処理に利用することができます。以下は、HTTPリクエストを処理するサーバーの例です。リクエストがキャンセルされた場合は、処理を中断します。

func handler(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()

  // リクエストの処理
  select {
  case <-time.After(time.Second * 5):
    fmt.Fprintln(w, "done")
  case <-ctx.Done():
    fmt.Fprintln(w, "canceled")
  }
}

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":8080", nil)
}

まとめ

以上が、contextの基本的な使い方についての説明です。contextを使用することで、複数のgoroutine間での情報共有やキャンセル処理を簡単に実装することができます。