Skip to content

Go contextパッケージ Walkthrough

2023/02/19

Go

目次

目次

  1. context パッケージについて
  2. Cancel
    1. WithCancel
    2. WithDeadline
    3. WithTimeout
    4. WithCancelCause
  3. Context
  4. データアクセス

context パッケージについて

context package - context - Go Packages は、Goroutine をキャンセルする API とコンテキスト内で管理しているデータへのアクセスを提供しています。

context パッケージは、並列処理を扱うなかで、done チャネルやデッドラインのパターンを組み込んだイディオムとして Go 1.7 で追加されました。

$ gocloc --not-match=test context
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Go                               1             59            254            340
-------------------------------------------------------------------------------
TOTAL                            1             59            254            340
-------------------------------------------------------------------------------

使い方と勘所ついては、こちらの書籍がとても分かりやすいです。

O’Reilly Japan - Go 言語による並行処理

Cancel

WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

context から context と cancel 関数を返します。 concel を実行することで、Context の Done チャネルが閉じられます。

WithCancel 関数内で、propagateCancel 関数を呼びます。
関数名の通りなのですが、propageteCancel では、親の Context がキャンセルされたときに子の Context をキャンセルさせます。

Context パッケージは、Done チャネルの提供とゴルーチンの親子関係のキャンセルを引き受けています。

WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

withCancel と同様な処理がされますが、指定した時刻を経過したら、cancel を実行し done チャネルを閉じます。

WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

引数に渡した時間経過後、cancel を実行し done チャネルを閉じます。

具体的な実装は、現在時刻に受け取った時間を足し、WithDeadline()を呼び出しています。

WithCancelCause

Go 1.20 で追加されました。withCancel と同様な処理ですが、CancelCause の引数に error を指定し、context がキャンセルされた原因を知ることができます。

Context

func Background() Context

空のコンテキストを返します。context.TODOcontext.Backgroundの実態は同じですが、コードに意図を持たせるために使い分けます。

var (
	background = new(emptyCtx)
	todo       = new(emptyCtx)
)

func Background() Context {
	return background
}

func TODO() Context {
	return todo
}

func TODO() Context

空の context を返します。一時的な実装をする場合などで利用できます。

func hello(ctx context.Context) {
	fmt.Println("Hello")
}

func main() {
	ctx := context.TODO()
	hello(ctx)
}
// Hello

withCancel, withDeadline, withTimeout の詳しい使い方は冒頭で紹介した書籍O’Reilly Japan - Go 言語による並行処理や、よくわかる context の使い方の記事が分かりやすいです。

データアクセス

func WithValue(parent Context, key, val any) Context

コンテキストにキーとデータのペアを含めることができます。

context.Valueメソッドの引数にWithValueのキーを渡すことでデータを context を通じて利用できます。

type key int
var testKey key

func TestCall(t *testing.T) {
	ctx := context.Background()
	ctx = context.WithValue(ctx, testKey, "value")
	hello(ctx)
}

func hello(ctx context.Context) {
	fmt.Println("Hello!")
	fmt.Println(ctx.Value(testKey))
}
// value