Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

いまさら聞けないselectあれこれ

12,507 views

Published on

Goにおける重要な構文、selectについて。今更聞けない基本からちょっとしたトリックまで。

Published in: Technology
  • Be the first to comment

いまさら聞けないselectあれこれ

  1. 1. いまさら聞けない select あれこれ Go Allstars 2 (Oct 2, 2016) 牧大輔 / Daisuke Maki (lestrrat) HDE株式会社
  2. 2. • 牧大輔 (lestrrat) • 株式会社 HDE • Perl/Go/C 等 • builderscon 12月3日 開催!
 https://builderscon.io/
  3. 3. builderscon • 12月3日開催! • Web系エンジニア達に楽しんでもらいつつ、これまでの枠にとらわれない 様々な分野の技術者達が終結! • 是非「知らなかった、を知る」ためにご来場ください。 • トーク募集中!
 https://builderscon.io/builderscon/tokyo/2016/cfp
  4. 4. =
  5. 5. Web+DB Press 10/22
  6. 6. channel++ • 端的に言ってチャンネルは最高 • 排他処理を意識しないデータの移動 • でもチャンネル単体ではできない操作が 結構ある。
  7. 7. func WriteCh(ch chan int) { ch <- 1 // Could block, bah } channels block • バッファ付きチャンネルでもブロックする可能性
  8. 8. func ReadCh(ch chan int) { <-ch // Could block, bah } channels block • 読み込みでも書き込みがあるまでブロック • 戻り値二つの<-chはチャンネルが閉じてあるかどうか の確認にしか使えないので、やっぱりブロックする
  9. 9. つまり • 複数のチャンネルを同時に同じgoroutineで 扱いたい場合はどうする?
  10. 10. go func() { <-ch1 … }() go func() { <-ch2 … }() チョットチガウ…
  11. 11. select • Cのselect使った事ある人: アレです • 複数のchannelの読み書きを制御してくれる • Go中級者なら確実にマスターしてるべき
  12. 12. select { case チャンネル操作: … case チャンネル操作: … default:
 … } 基本形
  13. 13. func CanWeRead(ch <-chan int) { select { case <-ch: … // yes, we can read } } 読み込み
  14. 14. func CanWeRead(ch <-chan int) { select { case <-ch: … // yes, we can read } } 読み込み ポイント:defaultがないとブ ロックするよ!
  15. 15. func CanWeRead(ch <-chan int) { select { case <-ch: … // yes, we can read default: … // no, we could not read } } 読み込み
  16. 16. func CanWeRead(ch <-chan int) { select { case <-ch: … // yes, we can read default: … // no, we could not read } } 読み込み ポイント:読み込みが即時でき ない場合はデフォルトが実行さ れるので、実質的なノンブロッ キングI/O
  17. 17. func CanWeRead(ch <-chan int) { select { case v, ok := <-ch: if !ok { // channel is closed … } fmt.Println(v) } } 値を使う
  18. 18. func CanWeRead(ch <-chan int) { select { case v, ok := <-ch: if !ok { // channel is closed … } fmt.Println(v) } } 値を使う ポイント:代入もできるよ!
  19. 19. func CanWeWrite(ch chan int) { select { case ch<-1: … // yes, we can write default: … // no, we could not write } } 書き込み
  20. 20. func CanWeWrite(ch chan int) { select { case ch<-1: … // yes, we can write default: … // no, we could not write } } 書き込み ポイント:書き込みが即時でき ない場合はデフォルトが実行さ れるので、実質的なノンブロッ キングI/O
  21. 21. ところで • nilなチャンネルに対する書き込み・読み込み の操作ってどうなるか知ってますか?
  22. 22. var ch chan int <-ch // 初期化してないのでch = nil • panic? • ブロック? • その他?
  23. 23. nil channel • 読み込み=ブロック • 書き込み=ブロック
  24. 24. var ch chan int select { case <-ch: … } …ということは
  25. 25. var ch chan int select { case <-ch: … } …ということは ポイント:この読み込みが成功するこ とはない!
  26. 26. select { case <-ch1: … case <-ch2: ch1 = nil // Disable previous case } 特定のケースを無効にする
  27. 27. select { case <-ch1: … case <-ch2: ch1 = nil // Disable previous case } 特定のケースを無効にする ポイント:最初は普通にch1から読み 込み可能
  28. 28. select { case <-ch1: … case <-ch2: ch1 = nil // Disable previous case } 特定のケースを無効にする ポイント:ch2から読み込みができた らch1を無効にしてる。これ以降ch1の caseは実行されることがない
  29. 29. ch1 = nil select { case <-ch1: … case <-ch2: ch1 = … // Enable previous case } 逆バージョン
  30. 30. ch1 = nil select { case <-ch1: … case <-ch2: ch1 = … // Enable previous case } 逆バージョン ポイント:最初はch1 = nilなので絶 対にこのcaseは実行されない。有効な チャンネルを代入すれば動く。
  31. 31. ch1 = nil select { case <-ch1: … case <-ch2: ch1 = … // Enable previous case } 逆バージョン ポイント:ch1 に有効なチャンネルを 代入すればch1のcaseも有効になる
  32. 32. パターン • selectを使ったよくつかうパターン集
  33. 33. t := time.NewTimer(30*time.Second) defer t.Stop() select { case <-t.C: return errors.New(“timeout”) case ch <- 1:
 return nil } 書き込みタイムアウト
  34. 34. t := time.NewTimer(30*time.Second) defer t.Stop() select { case <-t.C: return errors.New(“timeout”) case ch <- 1:
 return nil } 書き込みタイムアウト ポイント: 書き込みが成功したらエ ラー無しで脱出
  35. 35. t := time.NewTimer(30*time.Second) defer t.Stop() select { case <-t.C: return errors.New(“timeout”) case ch <- 1:
 return nil } 書き込みタイムアウト ポイント: 書き込むより先にタイム アウトになったらエラーを返す
  36. 36. t := time.NewTimer(30*time.Second) defer t.Stop() for { select { case <-t.C: // 30秒後にここにくる return default:
 // タイムアウトするまで行う操作 } } タイムアウトまでループ
  37. 37. t := time.NewTimer(30*time.Second) defer t.Stop() for { select { case <-t.C: // 30秒後にここにくる return default:
 // タイムアウトするまで行う操作 } } タイムアウトまでループ ポイント:for で囲まないと一回 defaultの操作をしたら終わっちゃう ので、無限ループにする
  38. 38. t := time.NewTimer(30*time.Second) defer t.Stop() for { select { case <-t.C: // 30秒後にここにくる return default:
 // タイムアウトするまで行う操作 } } タイムアウトまでループ ポイント: タイマーからお知らせが 来たらこのループから脱出
  39. 39. t := time.NewTicker(time.Second) defer t.Stop() for { select { case <-t.C: // 1秒ごとにここにくる // 定期的な処理内容 } } 定期的に実行
  40. 40. t := time.NewTicker(time.Second) defer t.Stop() for { select { case <-t.C: // 1秒ごとにここにくる // 定期的な処理内容 } } 定期的に実行 ポイント:TimerではなくTicker
  41. 41. ところで • closeされたチャンネルから読み込むとどうなる?
  42. 42. time.AfterFunc(5*time.Second, func() { close(done) }) for { select { case <-done:
 return default: // 定期的な処理内容 } } “done”チャンネル
  43. 43. time.AfterFunc(5*time.Second, func() { close(done) }) for { select { case <-done:
 return default: // 定期的な処理内容 } } “done”チャンネル ポイント: チャンネルをcloseすると 読み込みブロックが解除される
  44. 44. time.AfterFunc(5*time.Second, func() { close(done) }) for { select { case <-done:
 return default: // 定期的な処理内容 } } “done”チャンネル ポイント:戻り値は無効だが、ブロッ クは解けるのでループを脱出できる
  45. 45. for { select { case <-ctx.Done():
 return default: // 定期的な処理内容 } } Go 1.7からはContext
  46. 46. Context • context.Contextができてからselectを使う事 がより多くなった
  47. 47. Context • I/Oとか関係なくても、「途中で処理を止め る」系のコードでものすごく良く使う
  48. 48. 例:「リストを処理」 for _, data := range list { select { case <-ctx.Done():
 return default: process(data) } }
  49. 49. 例:「リストを処理」 for _, data := range list { select { case <-ctx.Done():
 return default: process(data) } } ポイント:リスト要素を処理する毎に Doneをチェックし、必要であれば終了
  50. 50. 外道 • いよいよselectの変態的な使いかたを必要とする時
  51. 51. reflect.Select • 動的にselect文を構築 • プログラム実行中にcaseの数や内容を変更できる reflect.Select([]reflect.SelectCase{ reflect.SelectCase{Chan: ch1}, reflect.SelectCase{Chan: ch2}, reflect.SelectCase{Chan: ch3}, })
  52. 52. Web+DB Press 10/22
  53. 53. =
  54. 54. まとめ • チャンネルとselectの複合技で表現の幅が 広がる • 初級→中級になるには押さえておきたい。
  55. 55. builderscon • 12月3日開催! • トーク募集中!
 https://builderscon.io/builderscon/tokyo/2016/cfp
  56. 56. Questions?

×