35
GoroutineChannelから はじめるGo言語 ver. 5 2015/11/26(木) @「最近、Go言語始めました」の会 The Go gopher was designed by Renee French . The gopher stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license.

Goroutineと channelから はじめるgo言語

Embed Size (px)

Citation preview

Page 1: Goroutineと channelから はじめるgo言語

GoroutineとChannelからはじめるGo言語ver. 52015/11/26(木)@「最近、Go言語始めました」の会

The Go gopher was designed by Renee French.The gopher stickers was made by Takuya Ueda.Licensed under the Creative Commons 3.0 Attributions license.

Page 2: Goroutineと channelから はじめるgo言語

アジェンダ

● 自己紹介

● Goとは?

● Goroutineの基本

● GoroutineとChannel

● 複数のChannelを扱う

● ファーストクラスオブジェクト

● 単方向のChannel

● for-selectパターン

Page 3: Goroutineと channelから はじめるgo言語

自己紹介

KLab株式会社KLabGames事業本部 エンジニア

上田拓也twitter: @tenntenn

■ 好きな言語

Go, JavaScript, Lua

■ 業務

モバイルオンラインゲームの開発(クライアントサイド)

Page 4: Goroutineと channelから はじめるgo言語

Goとは?

Googleが開発しているプログラミング言語

■ 特徴

● 並行プログラミング

● 豊富なライブラリ群

● 強力でシンプルな言語設計と文法

● クロスコンパイル/シングルバイナリ

● go tool

Page 5: Goroutineと channelから はじめるgo言語

Concurrency is not Parallelism

■ ConcurrencyとParallelismは違う● Concurrency => 並行

● Parallelism => 並列

■ Concurrency● 同時にいくつかの質の異なる事を扱う

■ Parallelism● 同時にいくつかの質の同じ事を行う

by Rob Pike

Page 6: Goroutineと channelから はじめるgo言語

Concurrency is not Parallelism

■ Concurrency

■ Parallelism

本を運ぶ

本を燃やす

台車を運ぶ

本を積む

本を燃やす 本を燃やす 本を燃やす 本を燃やす

Page 7: Goroutineと channelから はじめるgo言語

ConcurrencyとGoroutine

■ GoroutineでConcurrencyを実現● 複数のGoroutineで同時に複数のタスクをこなす

● 各Goroutineに役割を与えて分業する

■ 軽量なスレッドのようなもの● LinuxやUnixのスレッドよりコストが低い

● 1つのスレッドの上で複数のGoroutineが動く

■ Goroutineの作り方● goキーワードをつけて関数を呼び出す

go fnc()

複数のコアで動くとは限らない

Page 8: Goroutineと channelから はじめるgo言語

無名関数とGoroutinepackage mainimport "fmt"import "time"func main() {

go func() {fmt.Println("別のゴールーチン")

}()fmt.Println("mainゴールーチン")time.Sleep(50*time.Millisecond)

}Sleepしないとすぐに終了する

http://play.golang.org/p/jy1HWriRTS

Page 9: Goroutineと channelから はじめるgo言語

Goroutine-main

Goroutine間のデータのやり取り

Goroutine-2

go f2()

Goroutine-1

go f1()

Page 10: Goroutineと channelから はじめるgo言語

Goroutine-main

Goroutine間のデータのやり取り

Goroutine-2

go f2()

Goroutine-1

go f1()

変数v

print(v) v = 100

共有の変数を使う?

Page 11: Goroutineと channelから はじめるgo言語

共有の変数を使う

func main() {done := falsego func() {

time.Sleep(3 * time.Second)done = true

}()for !done {

time.Sleep(time.Millisecond)}fmt.Println("done!")

}

共有の変数を使う

http://play.golang.org/p/mGSOaq4mcr

Page 12: Goroutineと channelから はじめるgo言語

Goroutine-main

Goroutine間のデータのやり取り

Goroutine-2

go f2()

Goroutine-1

go f1()

変数v

print(v) v = 100

処理順序が保証されない!

競合

Page 13: Goroutineと channelから はじめるgo言語

共有の変数を使う

n := 1go func() {

for i := 2; i <= 5; i++ {fmt.Println(n, "*", i)n *= itime.Sleep(100)

}}()

http://play.golang.org/p/yqk82u0E4V

for i := 1; i <= 10; i++ {fmt.Println(n, "+", i)n += 1time.Sleep(100)

}

競合

Page 14: Goroutineと channelから はじめるgo言語

データ競合の解決方法

■ 問題点● どのGoroutineが先にアクセスするか分からない

● 値の変更や参照が競合する

■ 解決方法● 1つの変数には1つのGoroutineからアクセスする

● Channelを使ってGoroutine間で通信をする

● またはロックをとる(syncパッケージ)

"Do not communicate by sharing memory; instead, share memory by communicating"

Page 15: Goroutineと channelから はじめるgo言語

Goroutine-main

Channelとは?

Goroutine-2

go f2()

Goroutine-1

go f1()

Channel100

ch<-100<-ch

Goroutine間でデータの通信を行うパイプのようなもの

Page 16: Goroutineと channelから はじめるgo言語

Channelの特徴

■ 送受信できる型● Channelを定義する際に型を指定する

■ バッファ● Channelにバッファを持たせることができる

● 初期化時に指定できる

● 指定しないと容量0となる

■ 送受信時の処理のブロック● 送信時にChannelのバッファが一杯だとブロック

● 受信時にChannel内が空だとブロック

Page 17: Goroutineと channelから はじめるgo言語

Goroutine-main

送信時のブロック

Goroutine-2

go f2()

Goroutine-1

go f1()

Channel100

ch<-100

受信してくれるまでブロック

ブロック

Page 18: Goroutineと channelから はじめるgo言語

Goroutine-main

受信時のブロック

Goroutine-2

go f2()

Goroutine-1

go f1()

Channel100

<-ch

送信してくれるまでブロック

ブロック

Page 19: Goroutineと channelから はじめるgo言語

Channelの基本

■ 初期化

■ 送信

■ 受信

ch1 := make(chan int)ch2 := make(chan int, 10)

ch1 <- 10ch2 <- 10 + 20

n1 := <-ch1n2 := <-ch2 + 100

容量を指定

受け取られるまでブロック

一杯であればブロック

送信されまでブロック

空であればブロック

make(chan int, 0)と同じ

Page 20: Goroutineと channelから はじめるgo言語

Channelの基本

func main() {done := make(chan bool) // 容量0go func() {

time.Sleep(time.Second * 3)done <- true

}()<-donefmt.Println("done")

}

送信されるまでブロック

http://play.golang.org/p/k0sMCYe4PA

Page 21: Goroutineと channelから はじめるgo言語

Goroutine-main

複数のChannelから同時に受信

Goroutine-2

go f2()

Goroutine-1

go f1()

ブロックされるので同時に送受信出来ない?

Channel-1 Channel-2

ブロック

Page 22: Goroutineと channelから はじめるgo言語

Goroutine-main

複数のChannelから同時に受信

Goroutine-2

go f2()

Goroutine-1

go f1()

select-caseを使うと同時に送受信できる

Channel-1 Channel-2

select

Page 23: Goroutineと channelから はじめるgo言語

select-casefunc main() {

ch1 := make(chan int)ch2 := make(chan string)go func() { ch1<-100 }()go func() { ch2<-"hi" }()

select {case v1 := <-ch1:

fmt.Println(v1)case v2 := <-ch2:

fmt.Println(v2)}

}

http://play.golang.org/p/moVwtEdQIv

先に受信した方を処理

Page 24: Goroutineと channelから はじめるgo言語

nil Channelfunc main() {

ch1 := make(chan int)var ch2 chan stringgo func() { ch1<-100 }()go func() { ch2<-"hi" }()

select {case v1 := <-ch1:

fmt.Println(v1)case v2 := <-ch2:

fmt.Println(v2)}

}

http://play.golang.org/p/UcqW6WH0XT

nilの場合は無視される

ゼロ値はnil

Page 25: Goroutineと channelから はじめるgo言語

ファーストクラスオブジェクト

■ ファーストクラスオブジェクト● 変数に入れれる

● 引数に渡す

● 戻り値で返す

● ChannelのChannel

■ timeパッケージ

// 5分間待つ

<-time.After(5 * time.Minute)

http://golang.org/pkg/time/#After

chan chan int など

5分たったら現在時刻が

送られてくるChannelを返す

Page 26: Goroutineと channelから はじめるgo言語

Channelを引数や戻り値にする

func makeCh() chan int {return make(chan int)

}

func recvCh(recv chan int) int {return <-recv

}

func main() {ch := makeCh()go func() { ch <- 100 }fmt.Println(recvCh(ch))

}

http://play.golang.org/p/vg2RhcdNWR

Page 27: Goroutineと channelから はじめるgo言語

双方向のChannelfunc makeCh() chan int {

return make(chan int)}

func recvCh(recv chan int) int {go func() { recv <- 200 }()return <-recv

}

func main() {ch := makeCh()go func() { ch <- 100 }()fmt.Println(recvCh(ch))

}

http://play.golang.org/p/6gU92C6Q2v

間違った使い方ができる

Page 28: Goroutineと channelから はじめるgo言語

単方向のChannelfunc makeCh() chan int {

return make(chan int)}

func recvCh(recv <-chan int) int {return <-recv

}

func main() {ch := makeCh()go func(ch chan<- int) {ch <- 100}(ch)fmt.Println(recvCh(ch))

}

http://play.golang.org/p/pY4u1PU3SU

受信専用のChannel

送信専用のChannel

Page 29: Goroutineと channelから はじめるgo言語

Concurrencyを実現する

■ 複数Goroutineで分業する● タスクの種類によってGoroutineを作る

● Concurrencyを実現

■ Channelでやりとりする● Goroutine間はChannelで値を共有する

● 複雑すぎる場合はロックを使うことも

■ for-selectパターン● Goroutineごとに無限ループを作る

● メインのGoroutineはselectで結果を受信

Page 30: Goroutineと channelから はじめるgo言語

Goroutine-main

for-selectパターン

Goroutine-2

go f2()

Goroutine-1

go f1()

各Goroutineで無限ループを作る

Channel-1 Channel-2

select

for{} for{}

Page 31: Goroutineと channelから はじめるgo言語

つまりこういうこと

Goroutine-1

for{}

Goroutine-2

for{}

Goroutine-3

for{}

Goroutine-4

for{}

Channel

Channel

Channel

Channel

Page 32: Goroutineと channelから はじめるgo言語

まとめ

■ GoroutineでConcurrencyを実現● go f()で簡単に作れる

■ Channelでやりとりする● 送受信時のブロック

● ファーストクラスオブジェクト

● 単方向のChannel

■ for-selectパターン● Goroutineごとに無限ループを作る

● メインのGoroutineはselectで結果を受信

Page 33: Goroutineと channelから はじめるgo言語

何か作って発表しよう

■ Go Conferenceに参加しよう

http://eventdots.jp/event/573121

Page 34: Goroutineと channelから はじめるgo言語

何か作って記事を書こう

■ Go Advent Calendar● http://qiita.com/advent-calendar/2015/go● http://qiita.com/advent-calendar/2015/go2● http://qiita.com/advent-calendar/2015/go3

誰かgo4とgo5を!

Page 35: Goroutineと channelから はじめるgo言語