Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ホリネズミでもわかるGoroutine入門 / golang.tokyo#14

morikuni
April 16, 2018

ホリネズミでもわかるGoroutine入門 / golang.tokyo#14

morikuni

April 16, 2018
Tweet

More Decks by morikuni

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ ‣ Name: ৿ᅳ ହฏ (Morikuni Taihei) ‣ GitHub: @morikuni

    ‣ Twitter: @inukirom ‣ ॴଐ ‣ גࣜձࣾϝϧΧϦ ‣ GoͰόοΫΤϯυ։ൃ
  2. Goroutineͷىಈ go Λ͚ͭͯؔ਺Λݺͼग़͢͜ͱͰGoroutine͕ىಈ͢Δ go Function() go obj.Method() go func() {

    fmt.Println("hello goroutine") }() go func(name string) { fmt.Println("hello "+name) }("morikuni") // Ҿ਺΋౉ͤΔ
  3. ࣮ߦॱং͸อূ͞Εͳ͍ go func() { fmt.Println("goroutine 1") }() go func() {

    fmt.Println("goroutine 2") }() go func() { fmt.Println("goroutine 3") }() // goroutine 3 // goroutine 1 // goroutine 2
  4. GoroutineͱίʔϧελοΫ GoroutineΛ࢖Θͳ͍৔߹ͷίʔϧελοΫ func A() { B() } func B() {

    defer func(){}() C() } func C() { D() } func D() { panic("dead") }
  5. GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() func

    C() { func D() { panic("dead") } } } } ίʔϧελοΫ: A → B → C → D Bͷdefer͕࣮ߦ͞ΕΔ
  6. GoroutineͱίʔϧελοΫ GoroutineΛ࢖ͬͨ৔߹ͷίʔϧελοΫ func A() { B() } func B() {

    defer func(){}() go C() // goͰݺͼग़͢ } func C() { D() } func D() { panic("dead") }
  7. GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() }

    } func C() { func D() { panic("dead") } } ίʔϧελοΫ: C → D Bͷdefer͸࣮ߦ͞Εͳ͍
  8. chanͷ࢖͍ํ // chanͷ࡞੒ c := make(chan int) // chan΁ͷॻ͖ࠐΈ c

    <- 1 // chan͔ΒͷಡΈࠐΈ x := <- c for x := range c { ... } // chanΛด͡Δ close(c) ดͨ͡chan͔Β͸θϩ஋͕ಡΈࠐΊΔΑ͏ʹͳΓɺforϧʔϓ͕ऴྃ͢Δ ดͨ͡chanʹॻ͖ࠐΉͱpanic͢Δ
  9. 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)
  10. selectͷ࢖͍ํ select { case v := <-c1: fmt.Println(v) case c2

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

    <- "hello": default: fmt.Println("not ready") } c1͔ΒͷಡΈࠐΈ΋c2΁ͷॻ͖ࠐΈ΋Ͱ͖ͳ͍৔߹ʹdefault͕࣮ߦ͞ΕΔ
  12. Waitॲཧ ରࡦ1: Sleep͢Δ func main() { go func() { fmt.Println("Hello

    World") }() time.Sleep(time.Second) } ॲཧ͕ऴΘͬͨͷʹ଴ͪଓ͚Δ͜ͱʹͳΔ ॻ͖ࣺͯͷίʔυͰΑ͘࢖͏
  13. 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{}ͷ΄͏͕Ұൠత
  14. 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͢Δඞཁ͕͋Δ
  15. 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) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ
  16. Goroutineͱforϧʔϓ x͸ϧʔϓຖʹ஋Λ্ॻ͖͞Εͯ࢖͍ճ͞ΕΔ͔Β xs := []int{1, 2, 3, 4, 5} for

    _, x := range xs { go func() { fmt.Println(x) }() } time.Sleep(time.Second)
  17. 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) }() }
  18. 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) }() }
  19. ڝ߹ঢ়ଶ 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) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ
  20. ڝ߹ঢ়ଶ $ go run main.go [2 10] $ go run

    main.go [4 2 6 8 10] $ go run main.go [4 2 8] ͳͥʁ
  21. ڝ߹ঢ়ଶ 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) }
  22. ڝ߹ঢ়ଶ ରࡦ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Λ෼͚ͯಡΈࠐΈ͸ฒߦͰ ߦ͑Δ
  23. ڝ߹ঢ়ଶ ओʹ਺஋ܥ: 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ͳͲΛ૊Έ߹ΘͤΔ)
  24. 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) // ࠷ॳʹฦ͖ͬͯͨํ͚ͩΛ࢖͏ ͳʹ͕໰୊ʹͳΔͰ͠ΐ͏ʁ
  25. 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͢Δ
  26. 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͕ऴྃ
  27. 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
  28. 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) }