MyDocs

# Goのcontext

context パッケージ

context パッケージを見てみる。

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}

context パッケージのざっくりとした目的は以下の通り。

キャンセルには 3 つ側面がある。

使い方

Context の空インスタンスを作る関数は以下の 2 つ。

Done / Deadline メソッド

キャンセルに使う。

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

キャンセルする側は context.WithCancel() によって生成された cancal 関数を実行する. そのタイミングで、キャンセルされる側の context の Done メソッドが close される。

ctx, cancel := context.WithCancel(context.Background())
// 処理
cancel()

キャンセルされる側は ctx.Done() からキャンセルを受け取る。

<-ctx.Done()
func context.WithDeadline(parent context.Context, d time.Time) (context.Context, context.CancelFunc)

キャンセルする側は context.WithDeadline の生成時に停止したい時刻を設定することでその時刻を超えたタイミングでキャンセルが実行される。

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
func context.WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc)

キャンセルする側は context.Timeout の生成時に停止したい時間は設定することでその時間を超えたタイミングでキャンセルが実行される。

ctx, cancel := context.WithTimeout(context.Background(), time.Second)

ctx.WithDeadlinectx.Timeout を使ったキャンセルされる側は ctx.WithCancel と同様に ctx.Done() からキャンセルを受け取る。 context 生成時に得られる canacl は close されたチャネルには何も実行されないので、タイムアウトの処理をしていても明示的に cancel は呼ぶほうが良い。

context に対してタイムアウトが設定されているかどうかを確認するには、 context の Deadline メソッドを実行する。

type Context interface {
	Deadline() (deadline time.Time, ok bool)
}

設定されている場合、第 2 返り値は true で第 1 返り値にはその時刻が設定されている。

Err メソッド

type Context interface {
	Err() error
}
LOOP:
	for {
		select {
		case <-ctx.Done():
			if err := ctx.Err(); errors.Is(err, context.Canceled) {
				fmt.Println("Canceled")
			} else if errors.Is(err, context.DeadlineExceeded) {
				fmt.Println("DeadlineExceeded")
			}
			break LOOP
		}
	}

Value メソッド

func context.WithValue(parent context.Context, key interface{}, val interface{}) context.Context

WithValue を使うと context に key-value 形式でデータを保持できる。

ctx, cancel := context.WithCancel(context.Background())
ctx = context.WithValue(ctx, "id", 1)
ctx = context.WithValue(ctx, "user", "abc")

取り出すときはアサーションして値を取り出す。

id, user := ctx.Value("id").(int), ctx.Value("user").(string)