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