Go・Contextのメモ書き
Context
APIの境界、伝達
Goroutin
重たい処理に第一引数として渡す。
API呼び出しなど
スレッド
Goroutin
キャンセルしたときや、エラーが発生したとき、すぐにリソースを開放するため。
context.Background()
func (*Request) Context
context.Context型の値を返す
nilではない。
クライアントからの接続がキャンセルされたとき(HTTP/2)、またはServeHTTPがreturnされたときに
Contextがキャンセルされる
Value(key interface{}) interface{}のメソッドを実装
複数のgoroutineで使用できる。
一つのContextを複数のgoroutineに渡すことで、同時にキャンセルすることが可能
BackgroundのContextはキャンセルできない。
ルートのContext
context.WithCancel
context.WithValue
親Contextをコピー、keyとvalueをセットする。
ctx := context.WithValue(context.Background(), k, "Go")
v := ctx.Value(k);
code:go
func WithValue(parent Context, key, val interface{}) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
key, val interface{}
}
parentを埋め込んでいるContextを生成する!?
Context自体はpointer?
Contextインターフェイス
Err()
Doneが呼ばれる前はnilを返す
Doneが呼ばれたときや、timeoutしたとき、none-nilのerrを返す。
ctx.Doneを受信した時、ctx.Err()を返すのが一般的なパターンなのか?
code:go
func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
// Run the HTTP request in a goroutine and pass the response to f.
c := make(chan error, 1)
req = req.WithContext(ctx)
go func() { c <- f(http.DefaultClient.Do(req)) }()
select {
case <-ctx.Done():
<-c // Wait for f to return.
return ctx.Err()
case err := <-c:
return err
}
}
Timeoutパターン
contextをgoroutineがあるメソッドに渡す
メソッド内で、contextがdoneになるのを受け取る
キャンセルされる。
code:go
func main() {
ctx := context.Background()
ctxt, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
timeout := Timeout(Slow)
res, err := timeout(ctxt, "some input")
fmt.Println(res, err)
}
code:go
type WithContext func(context.Context, string) (string, error)
func Timeout(f SlowFunction) WithContext {
return func(ctx context.Context, arg string) (string, error) {
chres := make(chan string)
cherr := make(chan error)
go func() {
res, err := f(arg)
chres <- res
cherr <- err
}()
select {
case res := <-chres:
return res, <-cherr
case <-ctx.Done():
return "", ctx.Err()
}
}
}