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

DESAMIS Go Training Season 1 Day 9

DESAMIS Go Training Season 1 Day 9

デザミス社内 Go 研修 (第1期) の9日目の資料です。
今回は並行処理プログラミング、goroutine やチャネルを実際に触ってどのように並行で動く処理やそれらの通信を実現するかを学びます。

Yutaka Kato

June 22, 2021
Tweet

More Decks by Yutaka Kato

Other Decks in Technology

Transcript

  1. ݚमܭը ճ ೔࣍ ༧ఆ λΠτϧ ୈճ  Ր  (Pͷجຊ

    ୈճ  Ր  ؀ڥߏஙͱ(JU (JU)VCͷجຊ ୈճ  Ր  جຊߏจ ୈճ  Ր  ߏ଄ମɺεϥΠεɺϚοϓ ୈճ  Ր  ඪ४ϥΠϒϥϦ͸΍Ί͙Γ ୈճ  Ր  ϞδϡʔϧγεςϜͱ֎෦ϥΠϒϥϦ ୈճ  Ր  )FSPLV(Pೖ໳ ୈճ  Ր  "84-BNCEB(Pೖ໳ ୈճ  Ր  ฒߦॲཧϓϩάϥϛϯά ୈճ  Ր  ୯ମςετٕ๏ ୈճ  Ր  σʔλϕʔεΞΫηε ୈճ  Ր  'ZOFʹΑΔ(6*ΞϓϦέʔγϣϯ։ൃ ୈճ  Ր  ࣾ಺(PϓϩδΣΫτͷίʔυղઆ 
  2. $16ൃలτϨϯυ  従来手法 の 性能向上 が 頭打 ち に な

    り 、 マ ル チ コ ア 化 が 急速 に 進 ん で いる C P U の 性能 を 引 き 出 す に は ソ フ ト ウ ェ ア の マ ル チ コ ア 対応 が 不可欠 な 時代 に ,BSM3VQQ`Tl.JDSPQSPDFTTPS5SFOE%BUBz $$#: IUUQTHJUIVCDPNLBSMSVQQNJDSPQSPDFTTPSUSFOEEBUB ".%3Z[FO5ISFBESJQQFS9 ίΞεϨου 
 IUUQTXXXHENPSKQSFWJFX
  3. &SSBUB4FDVSJUZ`Tl4DBMBCJMJUZJUTUIFRVFTUJPOUIBUESJWFTVTzIUUQTCMPHFSSBUBTFDDPNTDBMBCJMJUZJUTRVFTUJPOUIBUESJWFTVTIUNM $,໰୊  ク ラ イ ア ン ト の

    同時接続数 が 1 0 , 0 0 0 ( 1 0 k ) を 超 え る と 、 
 サ ー バ ー を ス ケ ー ル ア ッ プ し て も 同時接続数 を 増 や せ な い 問題 サ ー バ ー の 性能 を2 倍 ( 赤 ) に し た の に 1 0 k 捌 け な い・ ・・ 
  4. $,໰୊  ク ラ イ ア ン ト の 同時接続数

    が 1 0 , 0 0 0 ( 1 0 k ) を 超 え る と 、 
 サ ー バ ー を ス ケ ー ル ア ッ プ し て も 同時接続数 を 増 や せ な い 問題 &SSBUB4FDVSJUZ`Tl4DBMBCJMJUZJUTUIFRVFTUJPOUIBUESJWFTVTzIUUQTCMPHFSSBUBTFDDPNTDBMBCJMJUZJUTRVFTUJPOUIBUESJWFTVTIUNM サ ー バ ー の 性能 を2 倍 ( 赤 ) に し た の に 1 0 k 捌 け な い・ ・・ N o d e . j s や N g i n x は 
 問題 な いん だ って ! 
  5. $,໰୊   サ ー バ ー ス レ ッ

    ド ス レ ッ ド ス レ ッ ド ス レ ッ ド ス レ ッ ド ઀ଓ ઀ଓ͋ͨΓ 
 εϨου ス レ ッ ド を1 つ 起 こ す に は 最低 で も 1 〜 2 M B は 必要 、 1 0 k な ら 最 低 で も 1 0 〜 2 0 G B ! 通信 に 対応 す る ス レ ッ ド を 探 す に は O(n2) の 探索 が 必要 C P U が 処理 す る ス レ ッ ド を 切 り 替 え る コ ン テ キ ス ト ス イ ッ チ の オ ー バ ー ヘ ッ ド が 増大 処理 の I / O 待 ち が 多 く な り 、 C P U の 利用効率 が 低下
  6. lHPz w (PͰฒߦॲཧΛ ߦ͏ʹ͸ʜ func f1() { fmt.Println("f1") } func

    f2() { fmt.Println("f2") } func main() { go f1() go f2() time.Sleep(100 * time.Microsecond) } +VTUlgoz $ go run main.go f1 f2 $ go run main.go f2 f1 
  7. lHPz w (PͰฒߦॲཧΛ ߦ͏ʹ͸ʜ func f1() { fmt.Println("f1") } func

    f2() { fmt.Println("f2") } func main() { go f1() go f2() time.Sleep(100 * time.Microsecond) } +VTUlgoz $ go run main.go f1 f2 $ go run main.go f2 f1  m a i n 
 g o r o u t i n e g o r o u t i n e 2 g o r o u t i n e 1 3 つ の g o r o u t i n e が 平行 で 動 いて いる !
  8. νϟωϧ func f1(done chan bool) { fmt.Println("f1") done <- true

    } func f2(done chan bool) { fmt.Println("f2") done <- true } func main() { done1 := make(chan bool) done2 := make(chan bool) go f1(done1) go f2(done2) <-done1 <-done2 }  w νϟωϧΛ࢖͏ͱɺฒߦॲཧؒ Ͱ஋΍ϝοηʔδΛ΍ΓͱΓͰ ͖Δ w ૹ৴จch <- x w ड৴จx = <-ch w ଴͚ͭͩͳΒ<-ch
  9. νϟωϧ w νϟωϧΛ࢖͏ͱɺฒߦॲཧؒ Ͱ஋΍ϝοηʔδΛ΍ΓͱΓͰ ͖Δ w ૹ৴จch <- x w

    ड৴จx = <-ch w ଴͚ͭͩͳΒ<-ch func f1(done chan bool) { fmt.Println("f1") done <- true } func f2(done chan bool) { fmt.Println("f2") done <- true } func main() { done1 := make(chan bool) done2 := make(chan bool) go f1(done1) go f2(done2) <-done1 <-done2 }  受信 を 待機 送信 送信 複数処理 を 平行 で 走 ら せ て 全部終 わ る の を
 待 つ 処理 が こ ん な に シ ン プ ル に ! チ ャ ネ ル を 渡 す
  10. TFMFDU w ଟॏԽෳ਺ͷνϟ ωϧΛ଴ͪड͚ɺ 
 ࠷ॳʹड৴ͨ͠΋ ͷΛर͏ func f1(done chan

    bool) { fmt.Println("f1") done <- true } func f2(done chan bool) { fmt.Println("f2") done <- true } func main() { done1 := make(chan bool) done2 := make(chan bool) go f1(done1) go f2(done2) select { case <-done1: fmt.Println("f1 is first!") case <-done2: fmt.Println(“f2 is first!") } } $ go run main.go f1 f1 is first! f2 
 $ go run main.go f2 f2 is first!  何 か の 条件 に 合 う ま で 待機
  11. DMPTF w νϟωϧʹ͸஋Λ ૹΓଓ͚Δ͜ͱ͕ Ͱ͖Δ w ૊ΈࠐΈؔ਺ close()Ͱνϟ ωϧͷऴྃΛ଴ͪ खʹ఻͑ΒΕΔ

    func async(counter chan int) { for i := 1; i <= 3; i++ { counter <- i time.Sleep(100 * time.Microsecond) } close(counter) } func main() { counter := make(chan int) go async(counter) for count := range counter { fmt.Println(count) } } $ go run main.go 1 2 3 $  3 回送信 チ ャ ネ ル を 閉 じ る 閉 じ ら れ る と 
 r a n g e が 抜 け る
  12. όοϑΝ͋Γνϟωϧ w όοϑΝαΠζΛࢦఆ͢ΔͱόοϑΝ͋ΓνϟωϧʹͳΔ w ड৴ʹରͯ͠ૹ৴͕ͷ৔߹͕Ӭԕʹૹ৴଴ͪʹͳΔͷΛ๷͛Δ func f1(done chan bool) {

    fmt.Println("f1") done <- true } func f2(done chan bool) { fmt.Println("f2") done <- true } func main() { done := make(chan bool, 2) go f1(done) go f2(done) <-done } $ go run main.go f2 $ go run main.go f2 f1  引数追加 1 つ 受信 し て 終了
  13. ฒߦॲཧͷཪଆ 1SPDFTT 1SPDFTT 1SPDFTT 5ISFBE 5ISFBE 5ISFBE $16 
 ίΞ

    $16 
 ίΞ ࣮ߦ ࣮ߦ εέδϡʔϦϯά 
 ίϯςΩετεΠον 
  14. ฒߦॲཧͷཪଆ 1SPDFTT 5ISFBE 5ISFBE $16 
 ίΞ $16 
 ίΞ

    ࣮ߦ NBJO 
 HPSPVUJOF HPSPVUJOF HPSPVUJOF ʜ ࣮ߦ ࣮ߦ ࣮ߦ ࣮ߦ ࣮ߦ ࣮ߦ 04εέδϡʔϥʔ HPϥϯλΠϜ 
 ./εέδϡʔϥʔ  ʜ
  15. ฒߦॲཧͷཪଆ w HPSPVUJOF͸ͱͯ΋ܰྔʂখ͞ͳؔ਺ͷ࣮ߦͰ΋͡ΌΜ͡ΌΜ্ཱͪ͛ͯ΋ ߏΘͳ͍ w ଞͷݴޠͷίϧʔνϯͱ͸ҟͳΓɺϓϩάϥϚ͕தஅ΍࠶։Λࢦࣔ͢Δ͜ͱ ͸Ͱ͖ͳ͍  ࣮ߦ୯Ґ ؅ཧ

    ࠷খελοΫ 
 αΠζ -JOVY ϓϦΤϯϓγϣϯ 1SPDFTT 04 .# ͋Γ 5ISFBE 04 .# ͋Γ HPSPVUJOF (Pϓϩηε ,# ϒϩοΫ͍ͯ͠Δ HPSPVUJOFͷΈ
  16.          

           (PMBOH +BWB 1ZUIPO           (PMBOH +BWB 1ZUIPO HPSPVUJOFͷҖྗ w େྔͷ.255௨৴Λߦ͏ϓϩάϥϜͰϦιʔεফඅΛൺֱ w ௨৴਺Λ૿΍ͯ͠΋ϝϞϦফඅͷ૿Ճ͕গͳ͘ɺͳ͓͔ͭ଎͍ʂ  ϝϞϦফඅྔ # ফඅ࣌ؒ T ݕূίʔυIUUQTHJUIVCDPNNJLBONRUUCFODINBSLT
  17. IUUQ-JTUFO"OE4FSWF  http.HandleFunc("/", handleHome ) log.Printf("ϙʔτ %s Ͱ଴ͪड͚Λ։࢝͠·͢...", port )

    if err := http.ListenAndServe(":"+port, nil); err != nil { log.Printf("αʔόʔ͕ҟৗऴྃ͠·ͨ͠: %v", err ) } ୈճϋϯζΦϯͷίʔυͷҰ෦ ඪ४ϥΠϒϥϦͷ࣮૷Λḷ͍ͬͯ͘ͱʜ
  18. IUUQ-JTUFO"OE4FSWF  http.HandleFunc("/", handleHome ) log.Printf("ϙʔτ %s Ͱ଴ͪड͚Λ։࢝͠·͢...", port )

    if err := http.ListenAndServe(":"+port, nil); err != nil { log.Printf("αʔόʔ͕ҟৗऴྃ͠·ͨ͠: %v", err ) } ୈճϋϯζΦϯͷίʔυͷҰ෦ func (srv *Server) Serve(l net.Listener) error { .. . for { rw, err := l.Accept( ) if err != nil { .. . } .. . go c.serve(connCtx ) } } TSDOFUIUUQTFSWFSHP -JTUFO"OE4FSWF TFSWFS-JTUFO"OE4FSWF TFSWFS4FSWF イ ベ ン ト ル ー プ は m a i n g o r o u t i n e 各 コ ネ ク シ ョン の 処理 は 別 の g o r o u t i n e
  19. )551Λ(PͰࡹ͘  m a i n 
 g o r

    o u t i n e ε Ϩου ε Ϩου ε Ϩου ε Ϩου g o r o u t i n e ઀ଓ ઀ଓ͋ͨΓ 
 HPSPVUJOF HPSPVUJOFͷελοΫαΠζ ͸े෼ʹখ͘͞ɺͨ͘͞Μىͯ͜͠ ΋େৎ෉ʂ ֤HPSPVUJOF͸(Pϥϯλ ΠϜ͕ޮ཰తʹ؅ཧ͍ͯ͠Δ ϒϩοΫ͠ͳ͍ݶΓ 
 ϓϦΤϯϓγϣϯ͞Εͳ͍ *0଴ͪͷ HPSPVUJOF͸$16ʹׂΓ౰ͯͳ ͍ͷͰແବ͕ͳ͍
  20. ϚΠΫϩαʔϏεͰͷ׆༻  サ ー ビ ス 1 外部 
 サ

    ー ビ ス 1 外部 
 サ ー ビ ス 2 外部 
 サ ー ビ ス 3 HPSPVUJOFͰॲཧ HPSPVUJOFͰॲཧ HPSPVUJOFͰॲཧ ク ラ イ ア ン ト へ 高速 に 応答 を 返 す に は 各依存 サ ー ビ ス の 平行呼 び 出 し が 効果的
  21. *P5Ͱͷ׆༻  ήʔτ΢ΣΠ $MPVE ͭड৴ͨ͠Β͙͢͞ ·࣍ͷड৴ʹඋ͑Δ ηϯ 
 αʔ ΤΫεϙωϯγϟϧ

    όοΫΦϑͳͲͷϦτϥ Π੍ޚ ΩϡʔΠϯάʹΑΔΦ ϑϥΠϯ࣌ͷσʔλ࠶ૹ σίʔυɺόϦσʔ γϣϯ౳ͷඇಉظԽ 頑強 な I o T ゲー ト ウ ェ イ 開発 に は 
 非同期処理 が 不可欠
  22. TZODඪ४ϥΠϒϥϦ  w ෳ਺ͷHPSPVUJOF͔Βಉ͡ม਺ΛಡΈॻ͖͢ΔͱͲ͏ͳΔ͔ʁ 
 ˠσʔλڝ߹ EBUBSBDF BOEPSڝ߹ঢ়ଶ SBDFDPOEJUJPO ʹʂ

    w .VUFYͳͲݪ࢝తͳ΋ͷ͔Βந৅౓ͷߴ͍΋ͷ·ͰऔΓἧ͑ͯ͋Δ ܕ આ໌ 4JODF .VUFY -PDL 6OMPDL͕Ͱ͖Δ  38.VUFY ಡग़͠ϩοΫ͕େଟ਺Λ઎ΊΔͱ͖ͷΈར༻  0ODF ॳظԽͳͲճͷΈ࣮ߦ͍ͨ͠ॲཧͷ࣮ݱ  8BJU(SPVQ ෳ਺ͷHPSPVUJOFͷ଴ͪ߹Θͤͷ࣮૷ʹศར  $POE ෳࡶͳ଴ͪ߹Θͤͷ৚݅ม਺ͷ࣮૷༻  1PPM σʔλ΍ΦϒδΣΫτͷ࠶ར༻ͷͨΊͷอ؅ݿ  .BQ ҆શʹಡΈॻ͖Ͱ͖Δ.BQ ͔ͪ͠͠ΐͬͱ࢖͍ͮΒ͍  ΑΓ௿Ϩϕϧͷػೳ͕ἧͬͨTZODBUPNJDαϒύοέʔδ΋νΣοΫʂ
  23. σουϩοΫͨ͠Β  w (P͕σουϩοΫΛݕ஌͢ΔͱQBOJD Ͱམͱͯ͘͠ΕΔ w ͨͩ͠*0଴ͪʹΑΔϩοΫͷ৔߹͸མͱͣ͞଴ͬͯ͘ΕΔ var resource sync.Mute

    x func f(done chan bool) { resource.Lock( ) defer resource.Unlock( ) done <- tru e } func main() { done := make(chan bool ) go f(done ) resource.Lock( ) defer resource.Unlock( ) <-don e } 👈͜ΕΛ࣮ߦ͢Δͱʜ
  24. σουϩοΫͨ͠Β  w (P͕σουϩοΫΛݕ஌͢ΔͱQBOJD Ͱམͱͯ͘͠ΕΔ w ͨͩ͠*0଴ͪʹΑΔϩοΫͷ৔߹͸མͱͣ͞଴ͬͯ͘ΕΔ var resource sync.Mute

    x func f(done chan bool) { resource.Lock( ) defer resource.Unlock( ) done <- tru e } func main() { done := make(chan bool ) go f(done ) resource.Lock( ) defer resource.Unlock( ) <-don e } fatal error: all goroutines are asleep - deadlock ! goroutine 1 [chan receive] : main.main( ) .../main.go:18 +0xb 8 goroutine 5 [semacquire] : sync.runtime_SemacquireMutex(0x10fa58c, 0x0, 0x1 ) ...
 main.f(0xc000054060 ) .../main.go:8 +0xa b .. . exit status 2 ϩοΫ͕औΕͳ͍ νϟωϧ͕ฦͬͯ͜ͳ͍ ˞ઌʹϩοΫΛऔͬͨͷ͕G ͩͬͨ৔߹͸ཱ৔͕ٯʹͳͬͨελοΫτϨʔε͕ग़Δ
  25. ࿅श໰୊ w ࿅श໰୊ 1 <ϑΥϧμ໊EBZFY> w ·ͣ͸11ͷCBOLΛࣸܦ͠·͢ ύοέʔδ͸NBJOʹม͑ͯ͠·͍·͠ΐ͏  w

    ώϯτҎԼͷΑ͏ͳߏ଄ମΛ࡞ͬͯΈ͍ͯͩ͘͞  type withdrawTransaction struct { amount in t ok chan boo l }
  26. ίʔυΛॻ͖ऴ͑ͨΒ w ʮϢʔβʔ໊QSBDUJDFʯϒϥϯνͰ࡞ۀ͍ͯ͠Δ͜ͱΛ֬ೝ͍ͯͩ͘͠͞ w 74$PEFͷҰ൪ࠨԼʹදࣔ͞Ε͍ͯ·͢ w มߋΛεςʔδ͠ɺίϛοτ͍ͯͩ͘͠͞ w 74$PEFͷࠨϝχϡʔͷʮιʔε؅ཧʯ͔Βૢ࡞ w

    ίʔυΛ(JU)VCʹϓογϡ͍ͯͩ͘͠͞ w 74$PEFͷҰ൪ࠨԼͷϒϥϯν໊ͷ͙͢ӈʹ͋ΔΞΠίϯ͔Βૢ࡞ ্ਤ  w ϓϧϦΫΤετΛ࡞੒͍ͯͩ͘͠͞ w ʮιʔε؅ཧʯͷ্෦ʹΞΠίϯ͕͋Γ·͢