😎

Go言語context.WithCancelはどうやって動いています?

2023/03/15に公開

コード1

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx := context.Background()

	ctx, cancel := context.WithCancel(ctx)

	go process(ctx)

	cancel()

	time.Sleep(time.Hour)
}

func process(ctx context.Context) {
	time.Sleep(time.Second)
	<-ctx.Done()
	fmt.Println("closed")
}

コードの動き

メイン関数のキャンセルにより、process関数の<-ctx.Done()が解除されます。

内部動き

  1. main関数にあるcancel()は先に実行されます。
  2. process関数にある<-ctx.Done()はあとで実行されます。

1が実行されるところはここです。
https://github.com/golang/go/blob/master/src/context/context.go#L461

クローズされるチャンネルはパッケージがローディング時に実行されます。

https://github.com/golang/go/blob/master/src/context/context.go#L379-L384

2が実行されるところはここです。
https://github.com/golang/go/blob/master/src/context/context.go#L408

コード2

func main() {
	ctx := context.Background()

	ctx, cancel := context.WithCancel(ctx)

	go process(ctx)

	time.Sleep(time.Second) // <- 追加した分
	cancel()

	time.Sleep(time.Hour)
}

func process(ctx context.Context) {
	time.Sleep(time.Second)
	<-ctx.Done()
	fmt.Println("closed")
}

コードの動き

メイン関数のキャンセルにより、process関数の<-ctx.Done()が解除されます。

内部動き

追加した分により、processにあるctx.Doneは先に実行されます。そして、main関数にあるcancelが実行されます

1が実行されるところはここです。
https://github.com/golang/go/blob/master/src/context/context.go#L415

2が実行されるところはここです。
https://github.com/golang/go/blob/master/src/context/context.go#L463

Discussion