Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ホリネズミでもわかるGoroutine入門 / golang.tokyo#14
Search
morikuni
April 16, 2018
Technology
9k
12
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ホリネズミでもわかるGoroutine入門 / golang.tokyo#14
morikuni
April 16, 2018
More Decks by morikuni
See All by morikuni
エラー設計について / Designing Errors
morikuni
7
3.2k
GopherCon 2018/2019
morikuni
0
100
絶対にExportされてないフィールドを書き換えるなよ!絶対だぞ!絶対! / golang.tokyo#20
morikuni
0
240
Architecture & Go kit
morikuni
2
2.1k
DIコンテナを使わないDI / golang.tokyo#11
morikuni
4
15k
Other Decks in Technology
See All in Technology
Rancherの紹介&Update情報(RancherJP Online Meetup #09)
yoshiyuki_kono
0
140
Djangoユーザが知っ得なPostgreSQL機能 - 設計の選択肢を増やす / Djang-use-PostgreSQL
soudai
PRO
0
210
Agentic Defenseとともにセキュリティエンジニアが輝き続けるには / How Security Engineers Can Keep Excelling with Agentic Defense
yuj1osm
0
130
機械学習を「社会実装」するということ 2026年夏版 / Social Implementation of Machine Learning June 2026 Version
moepy_stats
2
540
Dario Amodi『Policy on the AI Exponential』を理解する
nagatsu
0
210
【Gen-AX】20260530開催_JJUG CCC 2026 Spring
genax
1
450
実装は速くなった、レビューはどうする? ― 自身のレビューをAIで再現させるサーヴァントエンジニアリングのすゝめ / Implementation got faster. So what about reviews? — An invitation to Servant Engineering: Recreating your own code reviews with AI
nrslib
7
4.3k
関西に縁あるMicrosoft MVPsが語るCopilotの未来
kasada
0
1.2k
Agentic ERPをどう設計するか ー 受発注エージェントを動かす、現場の知見と設計思想ー
recerqainc
1
2k
新規事業を牽引する技術選定 〜フルスタックTypeScript開発の実践事例〜
nullnull
3
370
Oracle Cloud Infrastructure IaaS 新機能アップデート 2026/3 - 2026/5
oracle4engineer
PRO
1
230
LLMと共に進化するプロセスを目指して
ymatsuwitter
12
3.7k
Featured
See All Featured
VelocityConf: Rendering Performance Case Studies
addyosmani
333
25k
Git: the NoSQL Database
bkeepers
PRO
432
67k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
160
Prompt Engineering for Job Search
mfonobong
0
340
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
140
Testing 201, or: Great Expectations
jmmastey
46
8.2k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
65
55k
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
160
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
460
The untapped power of vector embeddings
frankvandijk
2
1.7k
Transcript
ϗϦωζϛͰΘ͔Δ Goroutineೖ golang.tokyo#14 2018/04/16
ࣗݾհ ‣ Name: ᅳ ହฏ (Morikuni Taihei) ‣ GitHub: @morikuni
‣ Twitter: @inukirom ‣ ॴଐ ‣ גࣜձࣾϝϧΧϦ ‣ GoͰόοΫΤϯυ։ൃ
ຊͷ༰ ॳ৺ऀ͕GoroutineΛ͑ΔΑ͏ʹͳΔͨΊͷ ‣ Goʹ͓͚Δฒߦॲཧʹ͍ͭͯ ‣ GoroutineΛͬͨฒߦॲཧͷҙͱରࡦ ‣ ศརͳπʔϧͷհ
Goʹ͓͚Δฒߦॲཧʹ͍ͭͯ
GoʹΈࠐ·Ε͍ͯΔฒߦॲཧͷͨΊͷػೳ ‣ Goroutine ‣ chan ‣ select
Goroutineʹ͍ͭͯ
Goroutineͱ ‣ GoΛද͢Δػೳͷ1ͭ ‣ ฒߦॲཧΛߦ͏ͨΊͷػೳ(CPU͕ෳ͋Εฒྻ࣮ߦͰ͖Δ) ‣ ϓϩηεεϨουΑΓܰྔ ‣ mainؔGoroutineͱ࣮ͯ͠ߦ͞Ε͍ͯΔ
Goroutineͷىಈ go Λ͚ͭͯؔΛݺͼग़͢͜ͱͰGoroutine͕ىಈ͢Δ go Function() go obj.Method() go func() {
fmt.Println("hello goroutine") }() go func(name string) { fmt.Println("hello "+name) }("morikuni") // ҾͤΔ
࣮ߦॱংอূ͞Εͳ͍ go func() { fmt.Println("goroutine 1") }() go func() {
fmt.Println("goroutine 2") }() go func() { fmt.Println("goroutine 3") }() // goroutine 3 // goroutine 1 // goroutine 2
GoroutineͱίʔϧελοΫ ‣ Goroutine͕ىಈͨ࣌͠ͰؔͷίʔϧελοΫ͕͢Δ ‣ panicGoroutineͷίʔϧελοΫΛ͍ͬͯ͘ ‣ ͭ·Γɺdefer & recoverpanic͕ൃੜͨ͠GoroutineͰ͏ඞཁ͕͋Δ ‣
Devquiz͜Ε͕ϋϚΓϙΠϯτ
GoroutineͱίʔϧελοΫ GoroutineΛΘͳ͍߹ͷίʔϧελοΫ func A() { B() } func B() {
defer func(){}() C() } func C() { D() } func D() { panic("dead") }
GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() func
C() { func D() { panic("dead") } } } } ίʔϧελοΫ: A → B → C → D Bͷdefer͕࣮ߦ͞ΕΔ
GoroutineͱίʔϧελοΫ GoroutineΛͬͨ߹ͷίʔϧελοΫ func A() { B() } func B() {
defer func(){}() go C() // goͰݺͼग़͢ } func C() { D() } func D() { panic("dead") }
GoroutineͱίʔϧελοΫ func A() { func B() { defer func(){}() }
} func C() { func D() { panic("dead") } } ίʔϧελοΫ: C → D Bͷdefer࣮ߦ͞Εͳ͍
chanʹ͍ͭͯ
chanͱ ‣ Goroutineؒͷσʔλͷૹड৴ॲཧͷಉظΛߦ͏ ‣ FIFOͷΩϡʔ(࠷ॳʹೖΕ͕ͨ࠷ॳʹऔΓग़͞ΕΔ) ‣ ༰ྔΛ࣋ͭ ‣ 3छྨͷܕ͕͋Δ ‣
Read Write ‣ Read only ‣ Write only
chanͷ͍ํ // chanͷ࡞ c := make(chan int) // chanͷॻ͖ࠐΈ c
<- 1 // chan͔ΒͷಡΈࠐΈ x := <- c for x := range c { ... } // chanΛด͡Δ close(c) ดͨ͡chan͔Βθϩ͕ಡΈࠐΊΔΑ͏ʹͳΓɺforϧʔϓ͕ऴྃ͢Δ ดͨ͡chanʹॻ͖ࠐΉͱpanic͢Δ
chanͷܕ c := make(chan int) // RW chan func Publish(c
chan <- int) { // WO chan ... } func Subscribe(c <-chan int) { // RO chan ... } // RW chanWOͱROʹೖՄೳ Publish(c) Subscribe(c)
selectʹ͍ͭͯ
selectͱ ‣ ෳͷchanͷૢ࡞Λѻ͏ͨΊͷͷ ‣ ࣮ߦՄೳʹͳͬͨchanͷૢ࡞Λ1͚࣮ͭͩߦ͢Δ ‣ defaultΛॻ͘͜ͱͰchanͷϒϩοΫΛ͛Δ
selectͷ͍ํ select { case v := <-c1: fmt.Println(v) case c2
<- "hello": } c1͔ΒͷಡΈࠐΈ or c2ͷॻ͖ࠐΈͷͲͪΒ͔Λ࣮ߦ͢Δ ࣮ߦͰ͖Δ·ͰϒϩοΫ͢Δ
selectͷ͍ํ select { case v := <-c1: fmt.Println(v) case c2
<- "hello": default: fmt.Println("not ready") } c1͔ΒͷಡΈࠐΈc2ͷॻ͖ࠐΈͰ͖ͳ͍߹ʹdefault͕࣮ߦ͞ΕΔ
͜͜·Ͱ͕Goʹ͓͚Δฒߦॲཧͷجૅ
GoroutineΛͬͨ ฒߦॲཧͷҙͱରࡦ
Waitॲཧ
Waitॲཧ func main() { go func() { fmt.Println("Hello World") }()
} ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ
Waitॲཧ $ go run main.go (ͳʹग़ྗ͞Εͳ͍) ͳͥʁ
Waitॲཧ Goroutineͷॲཧ͕࣮ߦ͞ΕΔલʹϓϩηε͕ऴྃͯ͠͠·͏͔Β func main() { go func() { fmt.Println("Hello World")
}() }
Waitॲཧ ରࡦ1: Sleep͢Δ func main() { go func() { fmt.Println("Hello
World") }() time.Sleep(time.Second) } ॲཧ͕ऴΘͬͨͷʹͪଓ͚Δ͜ͱʹͳΔ ॻ͖ࣺͯͷίʔυͰΑ͘͏
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{}ͷ΄͏͕Ұൠత
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͢Δඞཁ͕͋Δ
Waitॲཧͷ·ͱΊ ‣ αϯϓϧίʔυͳͲtime.Sleepָ͕ ‣ جຊతʹsync.WaitGroupΛ͍ͬͯΕ͍͍ ‣ chanෳͷGoroutineͷྃΛͭͷʹ͍ͮΒ͍ ‣ golang.org/x/sync/errgroupͱ͍͏WaitGroupʹΤϥʔॲཧΛՃͨ͠ ͷ͋Δ
Goroutineͱforϧʔϓ
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) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ
Goroutineͱforϧʔϓ $ go run main.go 5 5 5 5 5
ͳͥʁ
Goroutineͱforϧʔϓ xϧʔϓຖʹΛ্ॻ͖͞Ε͍ͯճ͞ΕΔ͔Β xs := []int{1, 2, 3, 4, 5} for
_, x := range xs { go func() { fmt.Println(x) }() } time.Sleep(time.Second)
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) }() }
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) }() }
ڝ߹ঢ়ଶ
ڝ߹ঢ়ଶ 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) ͳʹ͕ग़ྗ͞ΕΔͰ͠ΐ͏ʁ
ڝ߹ঢ়ଶ $ go run main.go [2 10] $ go run
main.go [4 2 6 8 10] $ go run main.go [4 2 8] ͳͥʁ
ڝ߹ঢ়ଶ 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) }
ڝ߹ঢ়ଶ R: double͔ΒಡΈࠐΜͩ W: doubleʹॻ͖ࠐΉ
ڝ߹ঢ়ଶ ରࡦ1: Ͱॲཧ͢Δ ‣ ฒߦॲཧΛ͢Δͱڝ߹͕͓͖ΔͷͰɺഉଞ੍ޚΛߦ͏ ‣ ഉଞ੍ޚΛҰ൪؆୯ʹΔํ๏ɺྻͰॲཧ͢Δ͜ͱͳͷͰຊʹGoroutine ͏ඞཁ͕͋Δ͔Λݕ౼ͯ͠ΈΔͱ͍͍
ڝ߹ঢ়ଶ ରࡦ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Λ͚ͯಡΈࠐΈฒߦͰ ߦ͑Δ
ڝ߹ঢ়ଶ ओʹܥ: 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ͳͲΛΈ߹ΘͤΔ)
ڝ߹ঢ়ଶͷ·ͱΊ ‣ ྻॲཧ(Goroutine͔ͭΘͳ͍)Ͱμϝ͔ݕ౼ ‣ ܥsync/atomic͕͑ΔՄೳੑ͕͋Δ ‣ ͦΕҎ֎sync.Mutex, sync.RWMutexΛ͑େମͳ͍
Goroutine leak
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) // ࠷ॳʹฦ͖ͬͯͨํ͚ͩΛ͏ ͳʹ͕ʹͳΔͰ͠ΐ͏ʁ
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͢Δ
Goroutine leak ରࡦ: context.ContextͱselectΛ͏ func fetch(ctx context.Context, url string, result
chan <- *http.Response) { res, err := http.Get(url) // ΄ΜͱGetcontextΛͬͨํ͕͍͍͚ͲྫͳͷͰׂѪ 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͕ऴྃ
GoroutineΛͬͨฒߦॲཧͷҙͱରࡦͷ·ͱΊ ‣ forͰมʹೖ͢Δ ‣ sync.WaitGroupͰྃΛͭ ‣ sync.MutexͰഉଞ੍ޚΛ͢Δ ‣ ܥsync/atomic͕ศར ‣
context.ContextͰGoroutine leakΛճආ͢Δ
ศརͳπʔϧͷհ
gotrace ‣ https://github.com/divan/gotrace ‣ GoroutineͱchanͷΓͱΓΛՄࢹԽͰ͖Δ
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
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()
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
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) }
gotraceΛಈ͔ͯ͠ΈΔ
Goroutine leakͱ͔ݟ͔ͭΔ͔?
ࠓͷ·ͱΊ ‣ GoݴޠతʹฒߦॲཧΛαϙʔτ͍ͯ͠Δ ‣ ฒߦॲཧ͍͚͠Ͳ͋ΔఔύλʔϯԽͰ͖Δ ‣ syncܥύοέʔδศར ‣ gotraceָ͍͠(খฒײ)
ʕ◔ϖ◔ʔ < Goroutine͍͖ͬͯ