- goでですね、
context.Context
がでてから割と立つんですが
- 並行まわりを全部これでやらないといけ無いんじゃ無いかという強迫観念にとりつかれませんか
- 素のchannnelやsync.WaitGroupとか使った方が早そうなんだけど
- 違う道があるんじゃ無いかと考えてしまう
- そこで、もう一度contextに対して色々いじって考えてみることにした
context.Contextとは何か?
- 簡単に言うと以下のメソッドを要求するinterface
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
- 要求するメソッドを実装してさえ居ればContextと見なされます
- これだけではなんなんで、言い換えてみると
- timeoutとcancelを表現する為の共通の枠組みです
実際の実装: WithCancel
- よく使われるContextに代入される実装は
- contextに生えているメソッドから利用できます
- その中でcancelを実装している
WithCancel
が分かりやすいかと思うので
- これを追います
- 構造体は
done
チャンネルを保持しています
type cancelCtx struct {
Context
mu sync.Mutex
done chan struct{}
children map[canceler]struct{}
err error
}
- 細かい部分をはしょりますが
- WithCancelが返すcancelの中身で以下の様に
done
channnelをcloseしています
if c.done == nil {
c.done = closedchan
} else {
close(c.done)
}
- 更にこのdoneは
Done
で参照されていて
- closeされた場合はcloseされたチャンネルが返ります
つまり、渡したctxを介してchannelを使用してキャンセル処理を行うことができます
func (c *cancelCtx) Done() <-chan struct{} {
c.mu.Lock()
if c.done == nil {
c.done = make(chan struct{})
}
d := c.done
c.mu.Unlock()
return d
}
- ここでよくあるcontextの定型処理をみます
- channnelがcloseされていればcase文の中身が実行されて終了します
select {
case <-ctx.Done():
fmt.Println("end")
return
}
ここでふたたびcontextとは何か?
- 現状
channelを介してgoroutineへキャンセル処理を伝播させることの出来る共通の書式
だと考えています。
- この仕組みの導入によりDoneに相当する処理が彼方此方で書かれなくてもよくなり
- contextを見たらそう言う処理があるんだろうなということが認識できるから分かりやすくなる
- 本題はここからで、この枠組みをもって並列化させる仕組みは綺麗に書くことが出来るのか
- sync.WaitGroupはどんな感じになっているかなどを次回以降に書く気になったら書きます
参考