Slide 1

Slide 1 text

ϗϦωζϛͰ΋Θ͔Δ Goroutineೖ໳ golang.tokyo#14 2018/04/16

Slide 2

Slide 2 text

ࣗݾ঺հ ‣ Name: ৿ᅳ ହฏ (Morikuni Taihei) ‣ GitHub: @morikuni ‣ Twitter: @inukirom ‣ ॴଐ ‣ גࣜձࣾϝϧΧϦ ‣ GoͰόοΫΤϯυ։ൃ

Slide 3

Slide 3 text

ຊ೔ͷ಺༰ ॳ৺ऀ͕GoroutineΛ࢖͑ΔΑ͏ʹͳΔͨΊͷ࿩ ‣ Goʹ͓͚Δฒߦॲཧʹ͍ͭͯ ‣ GoroutineΛ࢖ͬͨฒߦॲཧͷ஫ҙ఺ͱରࡦ ‣ ศརͳπʔϧͷ঺հ

Slide 4

Slide 4 text

Goʹ͓͚Δฒߦॲཧʹ͍ͭͯ

Slide 5

Slide 5 text

Goʹ૊Έࠐ·Ε͍ͯΔฒߦॲཧͷͨΊͷػೳ ‣ Goroutine ‣ chan ‣ select

Slide 6

Slide 6 text

Goroutineʹ͍ͭͯ

Slide 7

Slide 7 text

Goroutineͱ͸ ‣ GoΛ୅ද͢Δػೳͷ1ͭ ‣ ฒߦॲཧΛߦ͏ͨΊͷػೳ(CPU͕ෳ਺͋Ε͹ฒྻ࣮ߦ΋Ͱ͖Δ) ‣ ϓϩηε΍εϨουΑΓ΋ܰྔ ‣ mainؔ਺΋Goroutineͱ࣮ͯ͠ߦ͞Ε͍ͯΔ

Slide 8

Slide 8 text

Goroutineͷىಈ go Λ͚ͭͯؔ਺Λݺͼग़͢͜ͱͰGoroutine͕ىಈ͢Δ go Function() go obj.Method() go func() { fmt.Println("hello goroutine") }() go func(name string) { fmt.Println("hello "+name) }("morikuni") // Ҿ਺΋౉ͤΔ

Slide 9

Slide 9 text

࣮ߦॱং͸อূ͞Εͳ͍ go func() { fmt.Println("goroutine 1") }() go func() { fmt.Println("goroutine 2") }() go func() { fmt.Println("goroutine 3") }() // goroutine 3 // goroutine 1 // goroutine 2

Slide 10

Slide 10 text

GoroutineͱίʔϧελοΫ ‣ Goroutine͕ىಈͨ࣌͠఺Ͱؔ਺ͷίʔϧελοΫ͕෼཭͢Δ ‣ panic͸GoroutineͷίʔϧελοΫΛ໭͍ͬͯ͘ ‣ ͭ·Γɺdefer & recover͸panic͕ൃੜͨ͠Goroutine಺Ͱ࢖͏ඞཁ͕͋Δ ‣ Devquiz࿮͸͜Ε͕ϋϚΓϙΠϯτ

Slide 11

Slide 11 text

GoroutineͱίʔϧελοΫ GoroutineΛ࢖Θͳ͍৔߹ͷίʔϧελοΫ func A() { B() } func B() { defer func(){}() C() } func C() { D() } func D() { panic("dead") }

Slide 12

Slide 12 text

GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() func C() { func D() { panic("dead") } } } } ίʔϧελοΫ: A → B → C → D Bͷdefer͕࣮ߦ͞ΕΔ

Slide 13

Slide 13 text

GoroutineͱίʔϧελοΫ GoroutineΛ࢖ͬͨ৔߹ͷίʔϧελοΫ func A() { B() } func B() { defer func(){}() go C() // goͰݺͼग़͢ } func C() { D() } func D() { panic("dead") }

Slide 14

Slide 14 text

GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() } } func C() { func D() { panic("dead") } } ίʔϧελοΫ: C → D Bͷdefer͸࣮ߦ͞Εͳ͍

Slide 15

Slide 15 text

chanʹ͍ͭͯ

Slide 16

Slide 16 text

chanͱ͸ ‣ Goroutineؒͷσʔλͷૹड৴΍ॲཧͷಉظΛߦ͏ ‣ FIFOͷΩϡʔ(࠷ॳʹೖΕͨ஋͕࠷ॳʹऔΓग़͞ΕΔ) ‣ ༰ྔΛ࣋ͭ ‣ 3छྨͷܕ͕͋Δ ‣ Read Write ‣ Read only ‣ Write only

Slide 17

Slide 17 text

chanͷ࢖͍ํ // chanͷ࡞੒ c := make(chan int) // chan΁ͷॻ͖ࠐΈ c <- 1 // chan͔ΒͷಡΈࠐΈ x := <- c for x := range c { ... } // chanΛด͡Δ close(c) ดͨ͡chan͔Β͸θϩ஋͕ಡΈࠐΊΔΑ͏ʹͳΓɺforϧʔϓ͕ऴྃ͢Δ ดͨ͡chanʹॻ͖ࠐΉͱpanic͢Δ

Slide 18

Slide 18 text

chanͷܕ c := make(chan int) // RW chan func Publish(c chan <- int) { // WO chan ... } func Subscribe(c <-chan int) { // RO chan ... } // RW chan͸WOͱROʹ୅ೖՄೳ Publish(c) Subscribe(c)

Slide 19

Slide 19 text

selectʹ͍ͭͯ

Slide 20

Slide 20 text

selectͱ͸ ‣ ෳ਺ͷchan΁ͷૢ࡞Λѻ͏ͨΊͷ΋ͷ ‣ ࣮ߦՄೳʹͳͬͨchan΁ͷૢ࡞Λ1͚࣮ͭͩߦ͢Δ ‣ defaultΛॻ͘͜ͱͰchanͷϒϩοΫΛ๷͛Δ

Slide 21

Slide 21 text

selectͷ࢖͍ํ select { case v := <-c1: fmt.Println(v) case c2 <- "hello": } c1͔ΒͷಡΈࠐΈ or c2΁ͷॻ͖ࠐΈͷͲͪΒ͔Λ࣮ߦ͢Δ ࣮ߦͰ͖Δ·ͰϒϩοΫ͢Δ

Slide 22

Slide 22 text

selectͷ࢖͍ํ select { case v := <-c1: fmt.Println(v) case c2 <- "hello": default: fmt.Println("not ready") } c1͔ΒͷಡΈࠐΈ΋c2΁ͷॻ͖ࠐΈ΋Ͱ͖ͳ͍৔߹ʹdefault͕࣮ߦ͞ΕΔ

Slide 23

Slide 23 text

͜͜·Ͱ͕Goʹ͓͚Δฒߦॲཧͷجૅ

Slide 24

Slide 24 text

GoroutineΛ࢖ͬͨ ฒߦॲཧͷ஫ҙ఺ͱରࡦ

Slide 25

Slide 25 text

Waitॲཧ

Slide 26

Slide 26 text

Waitॲཧ func main() { go func() { fmt.Println("Hello World") }() } ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ

Slide 27

Slide 27 text

Waitॲཧ $ go run main.go (ͳʹ΋ग़ྗ͞Εͳ͍) ͳͥʁ

Slide 28

Slide 28 text

Waitॲཧ Goroutineͷॲཧ͕࣮ߦ͞ΕΔલʹϓϩηε͕ऴྃͯ͠͠·͏͔Β func main() { go func() { fmt.Println("Hello World") }() }

Slide 29

Slide 29 text

Waitॲཧ ରࡦ1: Sleep͢Δ func main() { go func() { fmt.Println("Hello World") }() time.Sleep(time.Second) } ॲཧ͕ऴΘͬͨͷʹ଴ͪଓ͚Δ͜ͱʹͳΔ ॻ͖ࣺͯͷίʔυͰΑ͘࢖͏

Slide 30

Slide 30 text

Waitॲཧ ରࡦ2: channelΛ࢖͏ func main() { c := make(chan struct{}) go func() { defer func() { c <- struct{}{} }() fmt.Println("Hello World") }() <-c // close͞ΕΔ·Ͱ଴ͭ } chanͰॲཧͷ׬ྃΛ௨஌͢Δ deferͰඞ࣮ͣߦ͞ΕΔΑ͏ʹ͢Δ chan boolΛ࢖͏Α͏ͳྫ΋͋Δ͚Ͳɺstruct{}ͷ΄͏͕Ұൠత

Slide 31

Slide 31 text

Waitॲཧ ରࡦ3: sync.WaitGroupΛ࢖͏ func main() { var wg sync.WaitGroup wg.Add(1) // GoroutineΛݺͼग़͢લʹAdd͢Δ go func() { defer wg.Done() // GoroutineͷதͰdeferΛ࢖ͬͯDoneΛݺͼग़͢ fmt.Println("Hello World") }() wg.Wait() } WaitGroup͸಺෦ͷΧ΢ϯλ͕0ʹͳΔ·Ͱwait͢Δ Done() = Add(-1)ͳͷͰAddͨ͠਺͚ͩDone͢Δඞཁ͕͋Δ

Slide 32

Slide 32 text

Waitॲཧͷ·ͱΊ ‣ αϯϓϧίʔυͳͲ͸time.Sleepָ͕ ‣ جຊతʹsync.WaitGroupΛ࢖͍ͬͯΕ͹͍͍ ‣ chan͸ෳ਺ͷGoroutineͷ׬ྃΛ଴ͭͷʹ͸࢖͍ͮΒ͍ ‣ golang.org/x/sync/errgroupͱ͍͏WaitGroupʹΤϥʔॲཧΛ௥Ճͨ͠΋ ͷ΋͋Δ

Slide 33

Slide 33 text

Goroutineͱforϧʔϓ

Slide 34

Slide 34 text

Goroutineͱforϧʔϓ 1, 2, 3, 4, 5Λग़ྗ͍ͨ͠(ॱෆಉ) xs := []int{1, 2, 3, 4, 5} for _, x := range xs { go func() { fmt.Println(x) }() } time.Sleep(time.Second) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ

Slide 35

Slide 35 text

Goroutineͱforϧʔϓ $ go run main.go 5 5 5 5 5 ͳͥʁ

Slide 36

Slide 36 text

Goroutineͱforϧʔϓ x͸ϧʔϓຖʹ஋Λ্ॻ͖͞Εͯ࢖͍ճ͞ΕΔ͔Β xs := []int{1, 2, 3, 4, 5} for _, x := range xs { go func() { fmt.Println(x) }() } time.Sleep(time.Second)

Slide 37

Slide 37 text

Goroutineͱforϧʔϓ forͷ࣮૷Πϝʔδ func iterate(xs []int, x *int) func() { i := -1 return func() { i ++ *x = xs[i] } } xs := []int{1, 2, 3, 4, 5} var x int next := iterator(xs, &x) for i := 0; i < len(xs); i ++ { next() go func() { fmt.Println(x) }() }

Slide 38

Slide 38 text

Goroutineͱforϧʔϓ ରࡦ: Ұ౓ม਺ʹ୅ೖ͢Δ for _, x := range xs { go func(x int) { // ؔ਺ͷҾ਺Ͱ͏͚ͱΔ fmt.Println(x) }(x) } for _, x := range xs { x := x // ผͷม਺ʹ୅ೖ͢Δ go func() { fmt.Println(x) }() }

Slide 39

Slide 39 text

ڝ߹ঢ়ଶ

Slide 40

Slide 40 text

ڝ߹ঢ়ଶ xsͷ஋Λ2ഒͯ͠doubleʹॻ͖ࠐΈ͍ͨ(ॱෆಉ) xs := []int{1, 2, 3, 4, 5} var double []int for _, x := range xs { go func(x int) { double = append(double, x*2) }(x) } time.Sleep(time.Second) fmt.Println(double) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ

Slide 41

Slide 41 text

ڝ߹ঢ়ଶ $ go run main.go [2 10] $ go run main.go [4 2 6 8 10] $ go run main.go [4 2 8] ͳͥʁ

Slide 42

Slide 42 text

ڝ߹ঢ়ଶ doubleΛಡΈࠐΜͰɺॻ͖ࠐΉ·Ͱʹdouble͕ߋ৽͞Ε͍ͯΔ͔Β for _, x := range xs { go func(x int) { double = append(double, x*2) }(x) } ࣮ࡍʹ͸ಡΈࠐΈ͔Βॻ͖ࠐΈ·Ͱʹ͕͔͔͍࣌ؒͬͯΔ for _, x := range xs { go func(x int) { tmp := double tmp = append(tmp, x*2) double = tmp }(x) }

Slide 43

Slide 43 text

ڝ߹ঢ়ଶ R: double͔ΒಡΈࠐΜͩ஋ W: doubleʹॻ͖ࠐΉ஋

Slide 44

Slide 44 text

ڝ߹ঢ়ଶ ରࡦ1: ௚઀Ͱॲཧ͢Δ ‣ ฒߦॲཧΛ͢Δͱڝ߹͕͓͖ΔͷͰɺഉଞ੍ޚΛߦ͏ ‣ ഉଞ੍ޚΛҰ൪؆୯ʹ΍Δํ๏͸ɺ௚ྻͰॲཧ͢Δ͜ͱͳͷͰຊ౰ʹGoroutine ࢖͏ඞཁ͕͋Δ͔Λݕ౼ͯ͠ΈΔͱ͍͍

Slide 45

Slide 45 text

ڝ߹ঢ়ଶ ରࡦ2: sync.MutexΛ࢖͏ var mu sync.Mutex xs := []int{1, 2, 3, 4, 5} for _, x := range xs { go func(x int) { mu.Lock() defer mu.Unlock() double = append(double, x*2) }(x) } Ұ౓ʹmu.Lock()Ͱ͖Δͷ͸1Օॴ͚ͩ ଞͷՕॴ͸mu.Unlock()͞ΕΔ·ͰϒϩοΫ͞ΕΔ sync.RWMutexΛ࢖͏ͱɺಡΈࠐΈͱॻ͖ࠐΈͷLockΛ෼͚ͯಡΈࠐΈ͸ฒߦͰ ߦ͑Δ

Slide 46

Slide 46 text

ڝ߹ঢ়ଶ ओʹ਺஋ܥ: sync/atomicύοέʔδΛ࢖͏ var count int64 atomic.AddInt64(&count, 1) // countʹ1Λ଍͢ atomic.CompareAndSwapInt64(&count, 1, 10) // countͷ஋͕1ͳΒ10ʹ͢Δ v := atomic.LoadInt64(&count) // countͷ஋ΛಡΈࠐΉ atomic.StoreInt64(&count, 100) // countͷ஋Λ100ʹ͢Δ float64ͳͲ͸αϙʔτ͞Ε͍ͯͳ͍͕ؤுΕ͹࢖͑Δ (floatΛ಺෦తʹ͸uintͱͯ͠ѻ͍ɺmath.Float64frombitsͱ atomic.LoadUint64ͳͲΛ૊Έ߹ΘͤΔ)

Slide 47

Slide 47 text

ڝ߹ঢ়ଶͷ·ͱΊ ‣ ௚ྻॲཧ(Goroutine͔ͭΘͳ͍)Ͱ͸μϝ͔ݕ౼ ‣ ਺஋ܥ͸sync/atomic͕࢖͑ΔՄೳੑ͕͋Δ ‣ ͦΕҎ֎͸sync.Mutex, sync.RWMutexΛ࢖͑͹େମ໰୊ͳ͍

Slide 48

Slide 48 text

Goroutine leak

Slide 49

Slide 49 text

Goroutine leak websiteAͱwebsiteBͷͲͪΒ͔ͷ݁ՌΛग़ྗ͍ͨ͠ type result struct { Response *http.Response Err error } func fetch(url string, c chan <- result) { res, err := http.Get(url) c <- result{res, err} } ch := make(chan result) go fetch("https: //websiteA", ch) go fetch("https: //websiteB", ch) fmt.Println( <-ch) // ࠷ॳʹฦ͖ͬͯͨํ͚ͩΛ࢖͏ ͳʹ͕໰୊ʹͳΔͰ͠ΐ͏ʁ

Slide 50

Slide 50 text

Goroutine leak type result struct { Response *http.Response Err error } func fetch(url string, c chan <- result) { res, err := http.Get(url) c <- result{res, err} } ch := make(chan result) go fetch("https: //websiteA", ch) go fetch("https: //websiteB", ch) fmt.Println( <-ch) // ࠷ॳʹฦ͖ͬͯͨํ͚ͩΛ࢖͏ websiteAͱwebsiteBͷͲͪΒ͔ยํͷGoroutine͕leak͢Δ

Slide 51

Slide 51 text

Goroutine leak ରࡦ: context.ContextͱselectΛ࢖͏ func fetch(ctx context.Context, url string, result chan <- *http.Response) { res, err := http.Get(url) // ΄Μͱ͸Get΋contextΛ࢖ͬͨํ͕͍͍͚ͲྫͳͷͰׂѪ select { case c <- result{res, err} case <-ctx.Done(): // context͕׬͍ྃͯͨ͠ΒॲཧΛ΍ΊΔ } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // ׬ྃޙʹcancelͯ͠contextΛdoneঢ়ଶʹ͢Δ ch := make(chan *http.Response) go fetch(ctx, "https: //websiteA", ch) go fetch(ctx, "https: //websiteB", ch) fmt.Println( <-ch) // ࠷ॳʹฦ͖ͬͯͨํ͚ͩΛ࢖͏ websiteA͕׬ྃ → fmt.Println → defer cancel() → ctx.Done() → websiteBͷfetch͕ऴྃ → Goroutine͕ऴྃ

Slide 52

Slide 52 text

GoroutineΛ࢖ͬͨฒߦॲཧͷ஫ҙ఺ͱରࡦͷ·ͱΊ ‣ forͰ͸ม਺ʹ୅ೖ͢Δ ‣ sync.WaitGroupͰ׬ྃΛ଴ͭ ‣ sync.MutexͰഉଞ੍ޚΛ͢Δ ‣ ਺஋ܥ͸sync/atomic͕ศར ‣ context.ContextͰGoroutine leakΛճආ͢Δ

Slide 53

Slide 53 text

ศརͳπʔϧͷ঺հ

Slide 54

Slide 54 text

gotrace ‣ https://github.com/divan/gotrace ‣ Goroutineͱchanͷ΍ΓͱΓΛՄࢹԽͰ͖Δ

Slide 55

Slide 55 text

gotraceͷ࢖͍ํ gotraceίϚϯυΛΠϯετʔϧ͢Δ ͨͿΜmasterϒϥϯνͩͱಈ͔ͳ͍ $ go get -u https: //github.com/divan/gotrace $ cd $(GOPATH)/github.com/divan/gotrace $ git checkout -b go18 origin/go18 $ go install

Slide 56

Slide 56 text

gotraceͷ࢖͍ํ ՄࢹԽ͍ͨؔ͠਺ͷઌ಄ʹ͜ΕΛॻ͘ import "runtime/trace" f, err := os.Create("trace.out") if err != nil { log.Fatal(err) } defer f.Close() trace.Start(f) defer trace.Stop()

Slide 57

Slide 57 text

gotraceͷ࢖͍ํ gotraceͰՄࢹԽ͢Δ ύονΛ౰ͯͨGoΛ࢖͏ඞཁ͕͋ΔͷͰdockerΛ࢖͏ $ cd path/to/main # main.go͕͋ΔσΟϨΫτϦ $ docker run --rm -it \ -e GOOS=darwin \ -v (pwd):/src \ divan/golang:gotrace \ go build -o /src/binary /src/main.go $ ./binary $ gotrace ./trace.out

Slide 58

Slide 58 text

gotraceΛಈ͔ͯ͠ΈΔ func Publish(c chan <- int) { c <- 1 time.Sleep(time.Millisecond) c <- 2 time.Sleep(time.Millisecond) c <- 3 time.Sleep(time.Millisecond) } func Subscribe(c <-chan int) { for v := range c { fmt.Println(v) } } func main() { f, err := os.Create("trace.out") if err != nil { log.Fatal(err) } defer f.Close() trace.Start(f) defer trace.Stop() c := make(chan int, 0) go Publish(c) go Subscribe(c) time.Sleep(5 * time.Millisecond) }

Slide 59

Slide 59 text

gotraceΛಈ͔ͯ͠ΈΔ

Slide 60

Slide 60 text

Goroutine leakͱ͔ݟ͔ͭΔ͔΋?

Slide 61

Slide 61 text

ࠓ೔ͷ·ͱΊ ‣ Go͸ݴޠతʹฒߦॲཧΛαϙʔτ͍ͯ͠Δ ‣ ฒߦॲཧ͸೉͍͚͠Ͳ͋Δఔ౓ύλʔϯԽͰ͖Δ ‣ syncܥύοέʔδศར ‣ gotrace͸ָ͍͠(খฒײ)

Slide 62

Slide 62 text

ʕ◔ϖ◔ʔ < Goroutine΍͍͖ͬͯ