チャネルの仕組み
by
Kenshi Kamata
×
Copy
Open
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Slide 1
Slide 1 text
チャネルのしくみ golang.tokyo #14
Slide 2
Slide 2 text
自己紹介 ● 鎌田健史 ● KLab 株式会社 ○ Unity でエディタ拡張書いたり ○ JS でミニゲーム作ってたり ○ 最近は Python ツールのサポート ● Women Who Go のハンズオンのお手 伝い
Slide 3
Slide 3 text
資料 動画
Slide 4
Slide 4 text
帰ってやってほしいこと ● GopherCon の発表を調べてみる ○ 20 分くらいの発表 + 資料 ● 実際に channel のソースコードを読んで見る
Slide 5
Slide 5 text
話したいこと ● チャネルのおさらい ● チャネルの構造 ○ バッファありチャネルのデータ保存の仕組み ● チャネルとデータの送受信の仕組み ○ シンプルなやり取り ○ 処理をブロックするとき
Slide 6
Slide 6 text
channel のおさらい https://go-tour-jp.appspot.com/concurrency/2 ● goroutine 間で競合を起こさずにデータをやり取りする仕組み ● Queue を持っていてデータを保存できる ● やり取りするデータの型を持っている goroutine1 goroutine2 chan
Slide 7
Slide 7 text
channel のおさらい ● データのやり取りは FIFO ● channel とやり取りできない場合、処理をブロックする ● データをやりとり出来るようになると処理を再開する ● close(chan) することで送信側の終了を伝えることができる ○ range ループで受け取っているときなど
Slide 8
Slide 8 text
コード例 Playgroundで試す
Slide 9
Slide 9 text
channel の構造 ● チャネルには2種類ある ○ バッファあり/なし ● 実装 ○ https://golang.org/src/runtime/chan.go#L31 ● chan T で生成されるものは実は hchan 構造体のポインタ ● メモリ上のヒープ領域に配置される
Slide 10
Slide 10 text
hchan の構造
Slide 11
Slide 11 text
バッファありチャネルの内部 ● buf ○ データを持つための配列への unsafe.Pointer ● lock ○ チャネルとデータのやり取りをするときにロックを取る ● sendx ○ チャネルに送った場合に配列のどこに置かれるか ● recvx ○ チャネルから取り出すときにどの位置から出すか
Slide 12
Slide 12 text
バッファありチャネル ● 生成時に容量を指定するとバッファありチャネル ● ch := make(chan string, 10) buf sendx = 0 lock recvx = 0 ・・・ 0 1 ・・・ 9
Slide 13
Slide 13 text
バッファありチャネル ● 要素を追加すると... buf sendx = 1 lock recvx = 0 0 1 ・・・ 9 ch <- “hello” 0 1 ・・・ 9
Slide 14
Slide 14 text
バッファありチャネル ● 要素を追加すると... buf sendx = 2 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 15
Slide 15 text
バッファありチャネル ● 一杯になると... buf sendx = 0 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 16
Slide 16 text
バッファありチャネル ● 要素を取りだすと... buf sendx = 0 lock recvx = 1 0 1 ・・・ 9 0 1 ・・・ 9
Slide 17
Slide 17 text
まとめ ● chan T の実態は hchan のポインタ ● バッファありチャネルのバッファは FIFO ● その実装はデータを取り出すためのインデックスとデータを受け取 るためのインデックスで実装されている
Slide 18
Slide 18 text
チャネルの送受信
Slide 19
Slide 19 text
シンプルな送受信 1. ロックを取る 2. バッファにコピー/バッファからコピー 3. ロック解除
Slide 20
Slide 20 text
送信 buf sendx = 0 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 21
Slide 21 text
送信 buf sendx = 0 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 22
Slide 22 text
送信 buf sendx = 0 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 23
Slide 23 text
送信 buf sendx = 1 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 24
Slide 24 text
送信 buf sendx = 1 lock recvx = 0 0 1 ・・・ 9 0 1 ・・・ 9
Slide 25
Slide 25 text
受信 buf sendx = 1 lock recvx = 0 0 1 ・・・ 9
Slide 26
Slide 26 text
受信 buf sendx = 1 lock recvx = 0 0 1 ・・・ 9
Slide 27
Slide 27 text
受信 buf sendx = 1 lock recvx = 0 0 1 ・・・ 9
Slide 28
Slide 28 text
受信 buf sendx = 1 lock recvx = 1 0 1 ・・・ 9
Slide 29
Slide 29 text
バッファを超える送信をした場合 buf sendx = 0 lock recvx = 0 0 1 ・・・ 9
Slide 30
Slide 30 text
バッファを超える送信をした場合 buf sendx = 0 lock recvx = 0 0 1 ・・・ 9 送信をブロックしなければいけない
Slide 31
Slide 31 text
寄り道: goroutine のスケジューリングの話 ● M:N thread モデル ○ M 個の OS thread で N 個のgoroutine を分担して動かす ● それぞれの OS thread に goroutine の Queue を持つ ● キリが良いところまで実行して次の goroutine を実行する
Slide 32
Slide 32 text
G M(OS thread) G G(goroutine) P(Context of Scheduling)
Slide 33
Slide 33 text
M 個の OS thread G M(OS thread) G G(goroutine) P(Context of Scheduling) G M(OS thread) G G(goroutine) P(Context of Scheduling) ・・・
Slide 34
Slide 34 text
参考資料 ● Goならわかるシステムプログラミング - Go言語と並列処理(2) ● Go のスケジューラ実装とハマりポイント
Slide 35
Slide 35 text
送信をブロックする 1. スケジューラに対して止めるよう指示する(gopark) 2. OS thread は対象の goroutine を止めて実行から外す 3. 次の goroutine を実行する
Slide 36
Slide 36 text
G M(OS thread) G G(goroutine) P(Context of Scheduling)
Slide 37
Slide 37 text
G G M(OS thread) G G(goroutine) P(Context of Scheduling) ch <- "schlusco"
Slide 38
Slide 38 text
channel から見たブロック ● goroutine を停止する命令を投げる ● 今の goroutine と送信用の値を Queue に保存
Slide 39
Slide 39 text
buf lock sendq recvq sudog g elem Function01 schlusco
Slide 40
Slide 40 text
goroutine の再開 ● シンプルに取りだす処理を行う ● sudog にある要素を取りだして buf に追加 ● sudog にある goroutine を実行待ち状態にする
Slide 41
Slide 41 text
buf lock sendq recvq sudog g elem Function01 schlusco 0 1 ・・・ 9
Slide 42
Slide 42 text
buf lock sendq recvq sudog g elem Function01 schlusco 0 1 ・・・ 9
Slide 43
Slide 43 text
buf lock sendq recvq sudog g elem Function01 schlusco 0 1 ・・・ 9
Slide 44
Slide 44 text
buf lock sendq recvq sudog g elem Function01 schlusco 0 1 ・・・ 9
Slide 45
Slide 45 text
G G M(OS thread) G G(goroutine) P(Context of Scheduling)
Slide 46
Slide 46 text
受信側がブロックする場合 ● 基本的に送信側がブロックする場合と逆
Slide 47
Slide 47 text
G M(OS thread) G G(goroutine) P(Context of Scheduling)
Slide 48
Slide 48 text
G G M(OS thread) G G(goroutine) P(Context of Scheduling) food := <-ch
Slide 49
Slide 49 text
buf lock sendq recvq sudog g elem Function02 food
Slide 50
Slide 50 text
goroutine を再開 ● 送信処理を行う ● sudog にある要素に buf の内容を渡す ● sudog にある goroutine を実行待ち状態にする
Slide 51
Slide 51 text
buf lock sendq recvq sudog g elem Function02 food 0 1 ・・・ 9
Slide 52
Slide 52 text
buf lock sendq recvq sudog g elem Function02 food 0 1 ・・・ 9
Slide 53
Slide 53 text
buf lock sendq recvq sudog g elem Function02 food := “sushi” 0 1 ・・・ 9
Slide 54
Slide 54 text
buf lock sendq recvq sudog g elem Function02 food := “sushi” 0 1 ・・・ 9
Slide 55
Slide 55 text
もっと賢く ● sudog の elem は受取先のポインタ ● なのでここに直接書いてしまえばOK! ● メモリコピーと lock を取る回数も減って嬉しい
Slide 56
Slide 56 text
buf lock sendq recvq sudog g elem Function02 food 0 1 ・・・ 9
Slide 57
Slide 57 text
buf lock sendq recvq sudog g elem Function02 food := “sushi” 0 1 ・・・ 9
Slide 58
Slide 58 text
バッファなしチャネルの話 ● 基本的にこれまで話してきたことと同じ ● バッファがないので常にブロックが発生する
Slide 59
Slide 59 text
Select で使う場合 ● 全部の channel をロックする ● 全部の channel の sudog に自分を入れて待ち状態にする ● 勝った case のところから処理を再開する
Slide 60
Slide 60 text
まとめ ● channel は使い方はシンプル ● だけど裏ではしっかり並列処理のための仕組みが動いている ● 発表資料はとても勉強になった ○ Gopher Con は行けないけど他の資料も見ていきたい