workerパターンをcontext化してみたら……
はい、というわけで、前記事のworkerパターンをcontextつかったらどーなるか、についてです。
前の記事や、その元記事のソースを読んでいる前提ですので、未読の方はそちらの確認からお願いします。
さて、ざっくりとした変更の方針ですけど、以下の2点です。
- contextを使うことで、workerの実装側でキャンセルできるようにする
- workerに渡す値はcontext経由にする。
というのをヤッツケでやってつくってみました。
以下変更点の解説です。
まず、元実装ではworkerで実行される処理がベタ書きだったのを汎用化するため、dispatcherのqueueに入れるjobをとqueueの定義を変更します。
type ( job struct { proc func(context.Context) ctx context.Context } Dispatcher struct { queue chan *job // ... } )
workerの実装も合わせて変更します。
go func() { defer d.wg.Done() for j := range d.queue { j.proc(j.ctx) } }()
さらにdispatcherは親contextとCancelFunc持つようにします。
type ( Dispatcher struct { // ... ctx context.Context cancel context.CancelFunc } )
そして使う側がcontextを拡張して使えるようにcontextを返す関数を用意します。
func (d *Dispatcher) Context() context.Context { return d.ctx }
job登録もcontextを使わないパターンと使うパターンで2種類用意します。
使わないパターンでは、dispatcherのctxをそのまま渡します。
func (d *Dispatcher) Add(proc func(context.Context)) { d.queue <- &job{proc: proc, ctx: d.ctx} } func (d *Dispatcher) AddWithContext(proc func(context.Context), ctx context.Context) { d.queue <- &job{proc: proc, ctx: ctx} }
終了処理も変更しています。
Immediatelyな実装の方では、queueの読み捨てをやめてcontextのcancelを先に呼ぶようにしました。
ちゃんとImmediatelyに帰ってくるかはjobの実装次第ですが、読み捨てよりはお行儀が良い気がしてます。
func (d *Dispatcher) StopImmediately() { d.cancel() close(d.queue) d.wg.Wait() }
Immediatelyじゃない実装もworkerが全部終了した後ですが、cancel()を実行しています。コレをしないとリークが発生します。
func (d *Dispatcher) Stop() { close(d.queue) d.wg.Wait() d.cancel() }
さて、ここまでやったので、元のgetをcontextを受け取って使うようにちょちょいっと修正して……
func get(ctx context.Context) { url := ctx.Value("url").(string) req, err := http.NewRequest("GET", url, nil) if err != nil { log.Fatal(err) } resp, err := http.DefaultClient.Do(req.WithContext(ctx)) // ... }
jobを登録するトコをこんなカンジで、context.WithValueでurlを渡すように修正します。
for i := 0; i < 10; i++ { url := fmt.Sprintf("http://placehold.it/%dx%d", i, i) ctx := context.WithValue(d.Context(), "url", url) d.AddWithContext(get, ctx) }
こんなカンジでworkerパターンのcontext対応化が完了しましたー。
感想
httpのclientとかはcontextに対応していて、中断処理に対応したモノをさっくり作ることができてラクですねー。
(8/24 追記)
id:lestrrat さんからマサカリが飛んできております((((;゚Д゚))))ガクガクブルブル
workerパターンをcontext化してみたら…… - okzkメモb.hatena.ne.jp
- [golang]
context.Contextを揮発性のないものでラップして持つのはよくないと思う。このほうがよりGoっぽいと思うが、どうだろう https://gist.github.com/lestrrat/c9b78369cf9b9c5d9b0c909ed1e2452e
2016/08/23 18:34
コチラについて、別エントリを書くかも……