Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ホリネズミでもわかるGoroutine入門 / golang.tokyo#14
Search
morikuni
April 16, 2018
Technology
9.1k
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
120
絶対にExportされてないフィールドを書き換えるなよ!絶対だぞ!絶対! / golang.tokyo#20
morikuni
0
270
Architecture & Go kit
morikuni
2
2.1k
DIコンテナを使わないDI / golang.tokyo#11
morikuni
4
15k
Other Decks in Technology
See All in Technology
【Oracle AI SPOTLIGHT ウェビナー】事例から見る!なぜOCIが選ばれたのか ― AI時代のクラウド戦略を紐解く ―
oracle4engineer
PRO
1
120
キヨさん、67歳。AIコーディングエージェントに挑む
happysamurai294
0
110
AI駆動開発におけるハーネスエンジニアリングの勘所
tsukuboshi
1
820
2026-10-24 JAWS JAWS-UG 茨城 #18 IAM Policy Autopilotってなにさ。terraform planからIAM Policyつくれるの? / What's IAM Policy Autopilot
masasuzu
0
180
Snowflake でできることが増えたら、 ABAC が必要になった
kaeton
0
110
仕様駆動開発の、その先へ。決める力と確かめる仕組み
inariku
0
110
E2Eってつらくないですか?E2Eの管理コストを下げる手段とその実践
akaboshinit
5
2.5k
演劇と私とスクラム
chinmo
0
160
渡米前の準備を制す!re:Invent 2026徹底準備術 / The Ultimate Preparation Guide for re:Invent 2026
yuj1osm
1
350
契約で守るコンパウンドプロダクトのデザインシステム
yug1224
1
1.6k
kintoneで在庫管理システムを作るときの要件定義・設計ポイント
kai0707
0
110
これだけは抑えておきたい!後悔しない re:Invent の過ごし方 / AWS re:Invent 2026 prestudy
kinunori
2
500
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
273
28k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
950
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
4
4.4k
[RailsConf 2023] Rails as a piece of cake
palkan
59
7.1k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
540
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.7k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1.1k
Agile that works and the tools we love
rasmusluckow
331
22k
Mind Mapping
helmedeiros
1
410
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
Making the Leap to Tech Lead
cromwellryan
135
10k
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͍͖ͬͯ