context パッケージを見てみる。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
context パッケージのざっくりとした目的は以下の通り。
キャンセルには 3 つ側面がある。
Context の空インスタンスを作る関数は以下の 2 つ。
func Background() Context
func TODO() Context
キャンセルに使う。
context.WithCancel
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()
context.WithDeadline
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))
context.Timeout
func context.WithTimeout(parent context.Context, timeout time.Duration) (context.Context, context.CancelFunc)
キャンセルする側は context.Timeout
の生成時に停止したい時間は設定することでその時間を超えたタイミングでキャンセルが実行される。
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
ctx.WithDeadline
、 ctx.Timeout
を使ったキャンセルされる側は ctx.WithCancel
と同様に ctx.Done()
からキャンセルを受け取る。
context 生成時に得られる canacl は close されたチャネルには何も実行されないので、タイムアウトの処理をしていても明示的に cancel は呼ぶほうが良い。
context に対してタイムアウトが設定されているかどうかを確認するには、 context の Deadline
メソッドを実行する。
type Context interface {
Deadline() (deadline time.Time, ok bool)
}
設定されている場合、第 2 返り値は true で第 1 返り値にはその時刻が設定されている。
type Context interface {
Err() error
}
nil
Canceled
DeadlineExceeded
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
}
}
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)