Slide 1

Slide 1 text

Concurrency 2018.07.18 @at_grandpa Crystal.tokyo #7 in 渋谷

Slide 2

Slide 2 text

@at_grandpa

Slide 3

Slide 3 text

圧倒亭グランパのブログ

Slide 4

Slide 4 text

Concurrency

Slide 5

Slide 5 text

✔ Concurrency の雰囲気を話します ✔ 細かい syntax などはドキュメント参照

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

“as in Go or Clojure” ✔ Go の goroutine/channel とほぼ同じ 終了!

Slide 8

Slide 8 text

Concurrency の中身を ちょっと覗いてみましょう

Slide 9

Slide 9 text

サンプルコード ✔ 並行処理 ✔ 各処理からの値の取得

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

String型のChannelをインスタンス化

Slide 12

Slide 12 text

2つのFiberを生成

Slide 13

Slide 13 text

Channel経由で値を取得

Slide 14

Slide 14 text

2018-07-18 08:12:50 +09:00 start 2018-07-18 08:12:50 +09:00 [fiber 1] start 2018-07-18 08:12:50 +09:00 [fiber 2] start 2018-07-18 08:12:55 +09:00 [top level] value: send from fiber 1 2018-07-18 08:12:55 +09:00 [fiber 1] end 2018-07-18 08:13:00 +09:00 [top level] value: send from fiber 2 2018-07-18 08:13:00 +09:00 end

Slide 15

Slide 15 text

2018-07-18 08:12:50 +09:00 start 2018-07-18 08:12:50 +09:00 [fiber 1] start 2018-07-18 08:12:50 +09:00 [fiber 2] start 2018-07-18 08:12:55 +09:00 [top level] value: send from fiber 1 2018-07-18 08:12:55 +09:00 [fiber 1] end 2018-07-18 08:13:00 +09:00 [top level] value: send from fiber 2 2018-07-18 08:13:00 +09:00 end ・並行に動いている ・値も取れている

Slide 16

Slide 16 text

どういう仕組みで動いているか

Slide 17

Slide 17 text

✔ Fiber
 ✔ Runtime Scheduler
 ✔ Event Loop ✔ Channel
 ✔ IO::Syscall Concurrencyを理解するポイント

Slide 18

Slide 18 text

Fiber ✔ Process ⊃ Thread ⊃ Fiber
 ✔ 協調マルチタスク
  - Fiber自ら、処理を他のFiberに委譲する  - 1つのFiberが固まるとシステム全体が固まる
 ✔ Crystalの処理は全てFiberで行われている
  - 「Main Fiber」でメインの処理を実行している

Slide 19

Slide 19 text

Runtime Scheduler ✔ Fiberの切り替えを担当
 ✔ クラス変数にFiberのqueueを持っている
  - @@runnables = Deque(Fiber).new
  - 実行可能Fiberのqueue ✔ Scheduler.rescheduleで切り替え

Slide 20

Slide 20 text

Event Loop ✔ I/O処理の委譲先
 ✔ 委譲している間に別のFiberを実行できる ✔ I/O処理が終了したら委譲元のFiberに移る

Slide 21

Slide 21 text

Channel ✔ Fiber間のデータのやりとり
 ✔ 送信元Fiberや受信先Fiberを保持  - @senders = Deque(Fiber).new
  - @receivers = Deque(Fiber).new ✔ 送受信時にFiberを切り替え
  - Runtime Scheduler を使う

Slide 22

Slide 22 text

IO::Syscall ✔ 以下でincludeされている
  - Crystal::System::FileDescriptor
  - Socket
 ✔ read/writeでFiber切り替え  - Runtime Scheduler を使う ほぼ全てのI/Oを網羅

Slide 23

Slide 23 text

実際の動き追う

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

Channelをインスタンス化

Slide 26

Slide 26 text

・Fiber1を定義 ・Runtime Scheduler の  実行可能Fiberのqueueに  Fiber1が enqueue される

Slide 27

Slide 27 text

concurrency.cr ・Fiber1を定義 ・Runtime Scheduler の  実行可能Fiberのqueueに  Fiber1が enqueue される

Slide 28

Slide 28 text

・Fiber2を定義 ・Runtime Scheduler の  実行可能Fiberのqueueに  Fiber2が enqueue される

Slide 29

Slide 29 text

・実際の処理はここから開始 ・Top Level のコードは  「Main Fiber」で動いている ・Channelの送受信時には  Fiberの切り替えが行われる

Slide 30

Slide 30 text

・実際の処理はここから開始 ・Top Level のコードは  「Main Fiber」で動いている ・Channelの送受信時には  Fiberの切り替えが行われる ・Runtime Scheduler の  実行可能Fiberのqueue  からshift ・Fiber1に処理が移る

Slide 31

Slide 31 text

・Fiber1に処理が移った

Slide 32

Slide 32 text

・sleep

Slide 33

Slide 33 text

concurrency.cr

Slide 34

Slide 34 text

concurrency.cr fiber.cr

Slide 35

Slide 35 text

concurrency.cr fiber.cr ・Event Loop に処理を委譲 ・Runtime Scheduler で  queueの次のFiber切り替え

Slide 36

Slide 36 text

concurrency.cr fiber.cr ・Event Loop に処理を委譲 ・Runtime Scheduler で  queueの次のFiber切り替え ・Fiber2へ移る

Slide 37

Slide 37 text

・Fiber2に処理が移った

Slide 38

Slide 38 text

・同じくFiber切り替え ・しかし、もう  実行可能Fiberのqueueには  Fiberが存在しない ・I/O処理を待機するしかない

Slide 39

Slide 39 text

待機中 . . .

Slide 40

Slide 40 text

・Event Loop が sleep 5 の  終了を検知 ・処理中のFiberが他にいないので  Fiber1の処理が再開される

Slide 41

Slide 41 text

・Channelに値を送信 ・sendの場合はreceiveを  呼んだFiberに切り替え ・Main Fiber に切り替わる ・このとき、sendしたFiberを  Schedulerのqueueにenqueue

Slide 42

Slide 42 text

・Channelに値を送信 ・sendの場合はreceiveを  呼んだFiberに切り替え ・Main Fiber に切り替わる ・このとき、sendしたFiberを  Schedulerのqueueにenqueue

Slide 43

Slide 43 text

・受信された値を表示

Slide 44

Slide 44 text

・Schedulerの  実行可能Fiberのqueueを元に  Fiberの切り替え ・queueにはsendした際に  enqueueされたFiber1が入っている

Slide 45

Slide 45 text

・Schedulerの  実行可能Fiberのqueueを元に  Fiberの切り替え ・queueにはsendした際に  enqueueされたFiber1が入っている ・Fiber1が再開

Slide 46

Slide 46 text

・値の表示

Slide 47

Slide 47 text

・Fiber1のブロックが終了 ・Fiberの切り替えが発生 ・しかし、Schedulerの  queueには実行可能Fiberが  存在しない

Slide 48

Slide 48 text

待機中 . . .

Slide 49

Slide 49 text

・Event Loop が sleep 10 の  終了を検知 ・処理中のFiberが他にいないので  Fiber2に処理が戻る

Slide 50

Slide 50 text

・Channelに値を送信 ・sendの場合はreceiveを  呼んだFiberに切り替え ・Main Fiber に切り替わる ・このとき、sendしたFiberを  Schedulerのqueueにenqueue

Slide 51

Slide 51 text

・Channelに値を送信 ・sendの場合はreceiveを  呼んだFiberに切り替え ・Main Fiber に切り替わる ・このとき、sendしたFiberを  Schedulerのqueueにenqueue

Slide 52

Slide 52 text

・受信された値を表示

Slide 53

Slide 53 text

・そのまま終了

Slide 54

Slide 54 text

・そのまま終了 ・ここは通らない

Slide 55

Slide 55 text

・そのまま終了 ・ここは通らない 2018-07-18 08:12:50 +09:00 start 2018-07-18 08:12:50 +09:00 [fiber 1] start 2018-07-18 08:12:50 +09:00 [fiber 2] start 2018-07-18 08:12:55 +09:00 [top level] value: send from fiber 1 2018-07-18 08:12:55 +09:00 [fiber 1] end 2018-07-18 08:13:00 +09:00 [top level] value: send from fiber 2 2018-07-18 08:13:00 +09:00 end [fiber 2] end は表示されてない

Slide 56

Slide 56 text

複雑!

Slide 57

Slide 57 text

とはいえ ✔ Concurrencyに必要な役者を知る
  - Fiber, Runtime Scheduler, Event loop, Channel
 ✔ Fiberが切り替わるタイミングを知る
  - I/Oの場合
   - 実行可能Fiber-queueからshift
  - receiveの場合
   - 実行可能Fiber-queueからshift
   - sendしたFiberをqueueにenqueue
  - sendの場合
   - receiveしたFiberに切り替え これらを知るだけで、だいぶ変わる

Slide 58

Slide 58 text

まとめ

Slide 59

Slide 59 text

✔ 並行処理 ✔ Concurrencyに必要な役者を知ろう
 ✔ Fiberを切り替えるタイミングを知ろう Concurrency

Slide 60

Slide 60 text

✔ 立て続けにsendされたらどうなるの?
  - sendされた値もqueueに保存される
  - receiveを呼ぶ度にqueueからshift ✔ Fiber内でI/O以外の重い処理があったら?  - 委譲できない処理はそのまま処理される
  - その処理が終わるまで他のFiberは実行できない
 ✔ 入れ子spawnとかどうなるんだろう?
  - \(^o^)/ まだまだあるよ

Slide 61

Slide 61 text

Happy Crystalling ! fin