- ゴルーチンを使ったコード
- レースコンディションの検知と修正
- チャネルによるデータ共有
・スケジューラーがどのゴルーチンがどのプロセッサで動いているか管理している
・CSP message passing model(共有データをロックするのではなくチャネルでの受け渡し)
スレッドと論理プロセッサと実行キュー
ブロック処理が入ると、スレッド&ゴルーチンは論理プロセッサから切り離されブロックが終わる(システムコールが返ってくる)のを待つ。
この間は論理プロセッサで実行しているスレッドは存在せず、
スケジューラーが実行キューにある他のゴルーチンを選択する。
ブロックが終了すると実行キューに再度入る
(ネットワークI/Oの場合は専用動作あり)
スレッド数の最大値はデフォルト1万(debug.SetMaxThreadsで変更可)
https://golang.org/pkg/runtime/debug/#SetMaxThreads
論理プロセッサが複数あれば、複数のスレッドで同時にゴルーチンが実行される
大文字と小文字を並行に出力する例
https://play.golang.org/p/JpdnN0EIar
GOMAXPROCS でスケジューラーが論理プロセッサを何個使うか設定
関数の前に go
をつけて実行すると新しいゴルーチンが作られて実行される
(実行キューにはLIFOで入る??)
sync.WaitGroup は数値カウントのセマフォで、
Waitを使うと数値が0より上の時、処理をブロックしてくれる。
https://golang.org/pkg/sync/#WaitGroup
ゴルーチン内では必ず defer を使うこと!
スケジューラーによって実行中のゴルーチンが切り替わる例
https://play.golang.org/p/6fyRhM87Lm
論理プロセッサが複数ある場合に並列に動く例
https://play.golang.org/p/E2ftTJnSvt
レースコンディション = 複数のゴルーチンが共有しているリソースに対して、非同期に同時に読み書きする時
共有リソースに対しては、通常は1つのゴルーチンのみが処理する必要がある
ローカル変数がコピーされずに毎回2で上書きされる例
https://play.golang.org/p/eUE0O_PRfg
(runtime.Goschedで強制的にゴルーチンの切り替えをしている)
-race オプション付きで 実行/ビルド するとレースコンディションを検知できる
atomic.AddInt64()
https://play.golang.org/p/AXE80vvrU2
atomic.LoadInt64() & atomic.StoreInt64()
https://play.golang.org/p/H3hDtoY-t7
sync.Mutex
https://play.golang.org/p/Dd0ocqMTVx
atomicやmutexのようなロックをしなくても、競合を解決できる方法がチャネル
チャネルにしたい型を指定してmakeで作成する
つかいかた
// 作成: make( chan [型] , [数] )
ch1 := make(chan int)
ch2 := make(chan int, 10)
// 送信: [チャネル] <- [値]
ch1 <- 1
// 受信: <-[チャネル]
<-ch1
送信・受信側、双方とも相手を待つ(ゴルーチンの実行をロックする)挙動になる
バッファなしチャネルでテニスゲームを模した(ボールの受け渡しをチャネルで行う)例 https://play.golang.org/p/kXltlBxiuw
リレー走を模した例 https://play.golang.org/p/I0wqElXclW
送信側はバッファがいっぱいにならない限りブロックしない 受信側はバッファが空にならない限りブロックしない
バッファありチャネルを使ったワーカーの例 https://play.golang.org/p/23xdrQcHsW
- 各ゴルーチンを独立して実行させることで並行処理を実現している
- 関数の呼び出し時にgo をつけるだけでゴルーチンになる
- ゴルーチンは単一のOSスレッドが使用している論理コアで動作し、キューを実行する(?)
- レースコンディションは複数のゴルーチンが同一のリソースにアクセスする時に起こる
- レースコンディションを防ぐには、atomicやmutexを使う
- チャネルは2つのゴルーチン間で安全にデータを共有するためのGoに組み込まれた方法
- バッファなしチャネルではデータの交換が保証されるが、バッファありチャネルでは保証されない。