Goroutineとchannelから始めるgo言語@初心者向けgolang勉強会

13,312 views

Published on

初心者向けgolang勉強会で発表したスライドです。
http://atnd.org/events/42889

Published in: Technology

Goroutineとchannelから始めるgo言語@初心者向けgolang勉強会

  1. 1. Goroutineと Channelから はじめるGo言語 2013/9/28(土) @初心者向けgolang勉強会
  2. 2. 自己紹介 上田拓也 KLab株式会社 仕事:  ・サーバサイド(PHP)  ・フロントエンド(JS) 学生のころ:  ・セルオートマトン  ・複雑系 twitter : @tenntenn
  3. 3. アジェンダ ● Go言語とは ● GorotuineとChannel
  4. 4. Go言語とは?
  5. 5. Go言語ってなに? ● え?Go言語知らない? ● ビッグウェーブに乗り遅れてますよ! Googleトレンド
  6. 6. Go言語とは? ● Googleが開発したコンパイラ言語 ○ その他のGoogleが開発した言語 ■ Dart : JSの代替 ■ noop : JavaVMで動く言語(忘却のかなた) ● 設計者: Robert Grisemer, Rob Pike, Ken Thompson
  7. 7. Go言語の特徴 ● シンプルな文法/設計 ○ 曖昧さを排除した設計 ○ 強い静的型付け ○ 型推論ができるので、型を省略できる ○ ポインタはあるがポインタ演算はなし ○ 複数の戻り値を返す関数 ○ ダックタイピング ○ ゼロ値初期化 ● ゴールーチンとチャネル ● 豊富なライブラリ群 ○ goツールは最強 ○ Web系パッケージの豊富さ ○ Google App Engine for go
  8. 8. 主なGo言語の勉強会@東京 ● GoCon spring, autumn ○ Go言語の勉強会では一番規模の大きい ○ Go言語の中の人が来る ● 質実Go研 ○ 標準ライブラリを中心にソースを読む会 ○ 少人数で濃い話をする ● 電車でGo! ○ 電車に乗りながらGo言語をする ● Go羅温泉 ○ 強羅温泉でGo言語をする
  9. 9. 私とGo言語 ● 研究で使うために始める ○ セルオートマトンシミュレータを作るため ● 主にGDG名古屋で活動していた ○ スタートGo #0, #1 ● CodeIQにて問題を出題 ● 社内でGo会を開催している
  10. 10. 初心者向けの情報 ● Tour of Go ○ Web上でGo言語の一通りいじれる ● Go言語の初心者が見ると幸せになれる場所 ○ http://qiita. com/tenntenn/items/0e33a4959250d1a55 045
  11. 11. Gorotuine と Channel
  12. 12. Concurrency is not Parallelism ● ConcurrencyとParallelismは違う by Rob Pike ○ Concurrency=並行? ○ Parallelism=並列? ● Concurrency ○ 同時にいくつかの事を扱う事 ● Parallelism ○ 同時にいくつかの計算を行なう事
  13. 13. Concurrency is... by Rob Pike Rob Pike氏のプレゼンのまとめでは、 ○ Concurrency is powerful. ○ Concurrency is not parallelism. ○ Concurrency enables parallelism. ○ Concurrency makes parallelism (and scaling and everything else) easy. とある。
  14. 14. Gorotuineとは? ● Go言語ではゴールーチンを用いてConcurrencyを実 現している ● スレッドに似ている... ○ しかし、もっとCheap(=コストが低い) ○ LinuxやUnixのスレッドとは違う ■ スレッドの上にいくつも乗っている ● 簡単に作成できる ○ 関数呼び出しの前にgoキーワードを付ける ■ go f()
  15. 15. go + クロージャ package main import "fmt" func main() { go func() { fmt.Println("別のゴールーチン") }() fmt.Println("mainゴールーチン") }
  16. 16. 並列度 ● 並列度:GOMAXPROCS ○ 同時に最大で実行可能なCPU数 ● デフォルトでは並列度は1になっている ○ runtime.NumCPU()で利用可能なCPU数が取得 できる ○ runtime.GOMAXPROCS()で並列度を設定する 【例】 runtime.GOMAXPROCS(runtime.NumCPU()) ● 指定した並列度でゴールーチンを動かす ○ あまり増やしても意味がない
  17. 17. Goroutine間のデータのやりとりは? Goroutine-1 Goroutine-2 go f1() go f2()
  18. 18. 共有の変数を使う? Goroutine-1 Goroutine-2 go f1() go f2() 変数v v = 100fmt.Println(v)
  19. 19. Goroutine間のデータのやりとり ● 共有の変数を使う? func main() { done := false go func() { time.Sleep(time.Second * 3) done = true }() for !done { time.Sleep(time.Millisecond) } }
  20. 20. 競合が起きますよね! Goroutine-1 Goroutine-2 go f1() go f2() 変数v v = 100v = 200 競合
  21. 21. Goroutine間のデータの競合 // これはダメ!! package main import "fmt" import "time" func main() { n := 1 go func() { for i := 2; i <= 5; i++ { fmt.Println(n, "*", i) n *= i } }() // 続く // 続き for i := 1; i <= 10; i++ { fmt.Println(n, "+", i) n += 1 time.Sleep(500) } } だめ!!
  22. 22. 何が問題なのか?どう解決するの? 【問題】 ● 同時に複数のGoroutineからアクセス ● 競合が起きる 【解決方法】 ● 1つのGoroutineからのみアクセスする ● Channelを使う!
  23. 23. Channelを使う Goroutine-1 Goroutine-2 go f1() go f2() ch <- 100 Channel <-ch 100 ● Goroutine間でデータをやりとりするパイプ?のような もの
  24. 24. Channelの特徴 ● 送受信するデータの型が決まっている ○ 型を指定してChannelを作る ● 一度に保持できる容量がある ○ 初期化の際に容量を指定、デフォルトは0 ● 送受信の際に相手の応答を待つ ○ 容量が0になると、受信元が受け取るまでブロックされる
  25. 25. 送信のブロック Goroutine-1 Goroutine-2 go f1() go f2() ch <- 100 Channel 100 ブロック ● 相手が受信(<-ch)してくれるまで、次に進めない
  26. 26. 受信のブロック Goroutine-1 Goroutine-2 go f1() go f2() Channel 100 ブロック ● 相手が送信(ch<-100)してくれるまで、次に進めない <-ch
  27. 27. Channelの宣言方法と使い方 ● 宣言 ○ ch := make(chan 型) // 容量0 ○ ch := make(chan 型, 容量) ● 送信 ○ ch <- 100 ○ ch <- "hoge" ■ 容量いっぱいであれば取り出されるの待機する ■ 容量0の場合は常に待つ ● 受信 ○ n := <- ch ■ 送られてくるまで待機する
  28. 28. Channelの宣言方法と使い方 func main() { done := make(chan bool) // 容量0 go func() { time.Sleep(time.Second * 3) done <- true }() <-done // 待つ fmt.Println("done") }
  29. 29. こういう場合はどうするの? Goroutine-1 Goroutine-2 go f1() go f2() Channel-1 ● ブロックするなら複数のChannelからデータを受け取 れない!? Goroutine-3 <-ch1 Channel-2 go f3() <-ch2 ch1 <- 100 ch2 <- "hoge"
  30. 30. selectを使う Goroutine-1 Goroutine-2 go f1() go f2() Channel-1 ● selectを使えば、同時に複数のchannelから受信でき る Goroutine-3Channel-2 go f3() <-ch2 ch1 <- 100 ch2 <- "hoge" select <-ch1
  31. 31. selectを使う ● 先に受信可能になったcaseを実行する // Goroutine-1にて select { case v1 := <-ch1: // Goroutine-2から ch1 <- 100 case v2 := <-ch2: // Goroutine-3から ch2 <- "hoge" } ※Channelがnilだとそのcaseは無視:nil channel
  32. 32. ReadOnlyとWriteOnly ● 通常のチャネルは双方向 ○ 引数や戻り値で渡す場合は困る func hoge(in chan int) { n := <-in // 入力として使うのが正解! in <- 100 // 間違った使い方ができる! }
  33. 33. ReadOnlyとWriteOnly ● ReadOnly ○ in := make(<-chan int) ● WriteOnly ○ out := make(chan<- int) ● キャスト ○ 双方向チャネルはキャストでReadOnlyまたは WriteOnlyに変化できる ○ ch := make(chan int) ○ in := (<-chan int)(ch) ○ out := (chan<- int)(ch)
  34. 34. ファーストクラスオブジェクト ● Channelはファーストクラスオブジェクト ○ 変数に入れれる ○ 引数に渡せる ○ 戻り値に取れる ○ ChannelのChannel ● 戻り値として、チャネルを返す関数は多用される ○ timeパッケージ ○ 5分間待つ <-time.After(5 * time.Minute)
  35. 35. range ● for文のrangeで送られてくるデータを次々に取 り出せる // 1分ごとに現在時間を出す for now := range time.Tick(1 * time.Minute) { fmt.Println(now) }
  36. 36. どう使って行くのか? ● 沢山の独立したゴールーチンがチャネルを使っ てやり取りをする! ○ Concurrencyになる ● ゴールーチンのコストは安いので、沢山作って もそんなに問題ない ○ ※コストが0ではない ● for - selectパターン
  37. 37. for - selectパターン Goroutine-1 Channel-1 Channel-2 select for{} Goroutine-2 for{} Goroutine-3 for{} ● 各Goroutineが無限ループになっており、イベントリス ナー的にChannelを使うパターン
  38. 38. Gopher君で表すとこんな感じ! ひたすら本を入れる ひたすら本を運ぶ ひたすら台車を運ぶ ひたすら本を燃やす
  39. 39. 構成要素の局所的な振る舞いが 相互作用する事によって 複雑な現象を作り出す 複雑系みたいで楽しい!
  40. 40. まとめ ● GoroutineはConcurrencyを実現する機構 ○ go f() で簡単に作れる ● ChannelはGoroutine間のデータのやり取りに 使う ○ 送受信のブロック ○ select〜case文 ○ ファーストクラスオブジェクト ○ select - forパターン

×