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

How to write synchronization mechanisms for Fiber (RubySapporo.beam)

How to write synchronization mechanisms for Fiber (RubySapporo.beam)

RubySapporo.beam #3 edition.

40f4d1f2e77078955bd01e9fb4a503ba?s=128

seki at druby.org

October 21, 2017
Tweet

Transcript

  1. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ ͝ཡͷεϙϯαʔͷఏڙ ʹΑΓ͓ૹΓ͠·͢

  2. slide sponsors Rails !! @mame Kent Beck @hisashim Ember.Sapporo @tricknotes

  3. slide sponsors Ruby @iwadon OSS Gate @ktou dRuby Web (

    eBook Store) http:// estore.ohmsha.co.jp/titles/978427406609P @hisashim @m_seki "re: " @awazeki Mastodon Pawoo Ruby @pixiv m_seki @tsuboi 07 smalruby @vestige_
  4. How to write synchronization mechanisms for Fiber seki@ruby-lang.org RubySapporo.beam#3

  5. @m_seki Ruby ERB, dRuby, Rinda ࠷ۙͷcommit: ERBϝϯςφʹk0kubun͞ΜΛadd

  6. ΤοηΠ2ຊࡌͤͨʂ ਺ਓ͔Β࣭໰͋Γ·͕ͨ͠ݪߘྉ͸1ຊ෼Ͱ͢

  7. 2005 - ·ͩॳ࡮Γങ͑·͢ dRuby ʹΑΔ ؔকढ़ஶ ෼ࢄ ɾ Web ϓϩάϥϛϯά

    12प೥
  8. 2012 (-2014 ઈ൛) 2೥Ͱઈ൛

  9. 2017 re-published on the web CC by SAͳͷͰ࿨༁Ͱ͖ΔΑ?

  10. Agenda Fiber Fiber Enumerator Synchronize Multiplexer

  11. Fiberͷ͓͞Β͍ Process Thread Fiber ࡾͭͷ࣮ߦओମͷҧ͍͔Βߟ͑Δ

  12. Process ↔ Thread ↔ Fiber Process Thread Fiber ͦͷٕज़͸ద੾ͳͷʁ

  13. Process ಌΕ͍ͯͨࠒͷ࿩

  14. Before Process Key https://www.youtube.com/watch?v=TOnS97C-RGQ

  15. Before Process (80s) OS PC BREAKΩʔ΋ϙʔϦϯά

  16. ϓϩηε΁ͷಌΕ

  17. None
  18. Before Process (90s) Fiber Macintoshͷෆ҆ఆ͞͸ϓϩηε͕͋Ε͹ղܾ͢Δͱ ݴΘΕͯͨ

  19. ࣮ࡍͷγεςϜ .. ֎෦ͱͷ΍ΓͱΓ͸ආ͚ΒΕͳ͍͠෭࡞༻͸ඞཁ

  20. Processes େࣄͳ͜ͱͳͷͰೋ౓ݴ͍·ͨ͠

  21. ProcessͰॻ͘ͱ͖ͷؾ࣋ͪ

  22. ProcessͰॻ͘ͱ͖ͷؾ࣋ͪ Skip!

  23. IPC͸ΊΜͲ͍͘͞ pipe SystemV IPC socket ͦΕͧΕಛ௃͕͋Γɺ࢖ͬͯΈͯΘ͔ΔΊΜͲ͘ ͕͋͞͞Δ

  24. Thread 1ϓϩηεͰίΞΛ࢖͍੾Βͳͯ͘΋͍͍͡ΌΜ

  25. ThreadͰॻ͘ͱ͖ͷؾ࣋ͪ ݟ͑Δ͚Ͳ৮Βͳ͍Ϟϥϧ

  26. Threadʹඞཁͳၗຫ͞ OS OS

  27. Fiber Thread Thread Thread mrubyʹ͸Threadͳ͍͔ΒFiberҰ୒

  28. Fiberʹඞཁͳࣗ৴ Ruby

  29. FiberͰॻ͘ͱ͖ͷؾ࣋ͪ ... Fiber ఆੴΛݟ͚ͭΔ·Ͱ͕͍ͨ΁Μ

  30. Process ↔ Thread ↔ Fiber Process Thread Fiber

  31. Process ↔ Thread ↔ Fiber Fiber Thread Process ͋ͳͨ͸ͳʹΛ࢖͏΂͖ʁ

  32. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ ෼͘Β͍͔ͳ ✓ 1SPDFTT 5ISFBE 'JCFS ✓ ࣍͸'JCFSΛקΊΔΑʂ

  33. ͳͥFiberͷ࿩Λ͢Δͷ͔ Fiber EventMachine͍ͬͯ͢͝@tricknotesʹڭΘͬͨΑ͏ͳ...

  34. ͳͥFiberͷ࿩Λ͢Δͷ͔ X11 GUI socket 1 Fiber͕͋Δͱॻ͖΍͘͢ͳΔ΋ͷΛΑ͘ॻ͍ͯͨ

  35. ͳͥFiberͷ࿩Λ͢Δͷ͔ Thread

  36. Fiberೖ໳ Fibonacci Enumerator Rdv SizedQueue, channel Multiplexer 3ͭͷςʔϚͰઆ໌͠·͢

  37. ࠷ॳͷςʔϚ Enumerator Fiber API Fiber Enumertor

  38. Α͋͘ΔFibonacci [0, 1] [1, 1] [2, 2] [3, 3] [4,

    5] [5, 8] [6, 13] [7, 21] [8, 34] [9, 55] ్தͷ஋Λฦ͢ͷ͕೉͍͠ def fibonacci(n) a, b = 1, 1 n.times do |i| p [i, a] a, b = b, a + b end end fibonacci(10)
  39. FiberͰॻ͖௚͢ API fib = Fiber.new do a, b = 1,

    1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end def fibonacci(n) a, b = 1, 1 n.times do |i| p [i, a] a, b = b, a + b end end fibonacci(10) FiberΛ࢖͏ͱ్தܦաͷ஋ΛฦͤΔ
  40. API Fiber.new Fiber#resume Fiber.yield

  41. Fiber.new Fiber fib = Fiber.new do a, b = 1,

    1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end Fiber࡞͚ͬͨͩ
  42. Fiber#resume Fiber fib = Fiber.new do a, b = 1,

    1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end FiberʹͳΓ͖ͬͯಈ͘
  43. Fiber.yield resume Fiber resume fib = Fiber.new do a, b

    = 1, 1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end resumeʹ໭Δ
  44. resume, again resume yield fib = Fiber.new do a, b

    = 1, 1 while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end
  45. resumeͷߦ͖ઌ Fiber new resume Fiber Fiber.yield resume ...

  46. APIΛઆ໌ͨ͠ resume yield

  47. EnumeratorͰॻ͖௚͢ fib = Fiber.new do a, b = 1, 1

    while true Fiber.yield(a) a, b = b, a + b end end 10.times do |n| p [n, fib.resume] end def fibonacci a, b = 1, 1 while true yield(a) a, b = b, a + b end end fib = to_enum(:fibonacci) 10.times do |n| p [n, fib.next] end
  48. Enumerator ❌ Fiber

  49. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ &OVNFSBUPSͰ"1*Λઆ໌ ✓ ೉ָ͕͠͞͠Ίͳ͍ ✓ ࣍͸3EW

  50. ࣍ͷςʔϚ

  51. QueueΛհͨ͠ڠௐ Queue Thread Thread q = SizedQueue.new(1) Thread.new do 10.times

    do |n| sleep(rand) q.push(n) p [:push, n] end end Thread.new do 10.times do |n| sleep(rand) p [:pop, q.pop] end end.join
  52. ͜ΕͷFiber൛ Rdv Fiber rendez-vous rdv = Rdv.new Fiber.new do 10.times

    do |n| rdv.push(n) p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume
  53. rendez-vous ⚓ rendez-vous

  54. ड͚औΔଆ͕଴ͭ৔߹ ⚓ rendez-vous

  55. ͜͏͍͏৔߹΋͋Δ ⚓ rendez-vous ΋ͪΖΜٯ΋͋Δ

  56. Rdvͷ࣮૷ initialize push pop def initialize @reader = [] @writer

    = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
  57. ࠷ۙ޷͖ͳΠσΟΦϜ resume yield

  58. ͱΓ͋͑ͣresume Fiber.new resume Thread.new resume

  59. initialize @reader pop @writer push Fiber def initialize @reader =

    [] @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
  60. push @reader (yield) @reader (resume) def initialize @reader = []

    @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
  61. @reader͕͍ͳ͍ Fiber.current yield def initialize @reader = [] @writer =

    [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
  62. @reader͕͍Δ @reader resume pop Fiber def initialize @reader = []

    @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end
  63. pop @writer (yield) @writer (resume) def initialize @reader = []

    @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end pushͱҰॹͩΑʂ Skip!
  64. @writer͕͍ͳ͍ Fiber.current yield def initialize @reader = [] @writer =

    [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Skip!
  65. @writer͕͍Δ @writer resume push Fiber def initialize @reader = []

    @writer = [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Skip!
  66. ݕࠪதͷഉଞ੍ޚ Fiber Thread def initialize @reader = [] @writer =

    [] end def push(it) if @reader.empty? @writer << [it, Fiber.current] return Fiber.yield end @reader.shift.resume(it) end def pop if @writer.empty? @reader << Fiber.current return Fiber.yield end value, fiber = @writer.shift fiber.resume return value end Thread೴ͷਓ͸υΩυΩͯ͠Δͱࢥ͏
  67. Threadͱͷҧ͍ Thread Monitor

  68. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume Fiber.newした
 kara-matzを召喚
  69. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzをresume
 kara-matzのターン

  70. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzのpush!
 相手がいない。
 自分とデータをメモしyield。ターン終了
  71. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume メインのターン
 Fiber.new
 ichi-matzを召喚
  72. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzをresume
 ichi-matzのターン

  73. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzのpop!
 ichi-matzはメモを読んだ
 データを手にいれた。kara-matzをresumeしターン終了
  74. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume kara-matzのpush!
 相手がいない。
 自分とデータをメモしyield。ターン終了
  75. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzのpop!
 ichi-matzはメモを読んだ
 データを手にいれた。kara-matzをresumeしターン終了 Skip!
  76. 10 Skip!

  77. ͏༷͘͝ࢠ rdv = Rdv.new Fiber.new do 10.times do |n| rdv.push(n)

    p [:push, n] end end.resume Fiber.new do 10.times do |n| p [:pop, rdv.pop] end end.resume ichi-matzはしんでしまった
 メインのターン
 メインはしんでしまった Skip!
  78. ͏͖ͦ͝͏ʂ ͕͢͞

  79. ͜͜ͷ·ͱΊ Fiber resume yield

  80. ͜͜ͷ·ͱΊ Thread Thread Fiber ޿ౡͰ͸@_ko1͔ΒAutoFiberͱ͍͏ఏҊΛ঺հͯ͠ ͍ͨɻϒϩοΫͦ͠͏ʹͳͬͨΒࣗಈతʹίϯς Ωετ੾Γସ͑͢ΔFiberɻ͜͜͸ͦΕ΁ͷճ౴

  81. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ 'JCFSͷಉظͷ࣮૷ ✓ 3EW ✓ ࣍͸ιέοτϓϩάϥϛ ϯά

  82. ࣍ͷςʔϚ socket

  83. ͢͢Ί͔ͨ dRuby Thread Thread Fiber

  84. ૉ๿͚ͩͲҰ్ I/O 1 def main_loop loop do select fds.each do

    |fd| call_callback end end end def on_accept c = accept loop do read_request(c) do_it write_response(c) end end def read_n(fd, sz) ... ... end
  85. Thread൛ fd Thread def main_loop loop do select fds.each do

    |fd| call_callback end end end def on_accept c = accept Thread.new(c) do |fd| loop do read_request(fd) do_it write_response(fd) end end end def read_n(fd, sz) ... ... end ௥Ճ͸͚ͩ͜͜ ௥Ճ͸͚ͩ͜͜
  86. Fiber൛ def main_loop loop do select fds.each do |fd| call_callback

    end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... ... end Swapped with Fiber
  87. Fiber൛վ I/O I/O def main_loop loop do select fds.each do

    |fd| call_callback end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end
  88. Block-ish non-block I/O Fiber.yield main_loop resume def main_loop loop do

    select fds.each do |fd| call_callback end end end def on_accept c = accept Fiber.new do loop do read_request(c) do_it write_response(c) end end.resume end def read_n(fd, sz) ... fd.read_nonblock(sz) rescue WaitReadable Fiber.yield retry end
  89. ίʔϧόοΫͰresume

  90. Bartender fd readable, writable https://github.com/seki/bartender

  91. Bartender::Context readable, writable block-ish non-block I/O Context#[]=(event, fd, callback) Πϕϯτछʢ:read,

    :writeʣͱfdͷ૊ʹίʔϧόοΫΛొ࿥
  92. _read() read_nonblock readable retry Fiber def _read(fd, sz) return fd.read_nonblock(sz)

    rescue IO::WaitReadable wait_readable(fd) retry end def wait_readable(fd); wait_io(:read, fd); end def wait_io(event, fd) self[event, fd] = Fiber.current.method(:resume) Fiber.yield ensure delete(event, fd) end
  93. read_nonblock sz IO::WaitReadable def _read(fd, sz) return fd.read_nonblock(sz) rescue IO::WaitReadable

    wait_readable(fd) retry end def wait_readable(fd); wait_io(:read, fd); end def wait_io(event, fd) self[event, fd] = Fiber.current.method(:resume) Fiber.yield ensure delete(event, fd) end
  94. wait_io() resume Fiber.yield select resume readable def _read(fd, sz) return

    fd.read_nonblock(sz) rescue IO::WaitReadable wait_readable(fd) retry end def wait_readable(fd); wait_io(:read, fd); end def wait_io(event, fd) self[event, fd] = Fiber.current.method(:resume) Fiber.yield ensure delete(event, fd) end
  95. I'll show this to you on an actual application

  96. tiny_drbͷൈਮ read def req_drb(reader) ref = load(reader, false) msg =

    load(reader) argc = load(reader) argv = argc.times.collect {load(reader)} block = load(reader, false) [ref, msg, argv] end def load(reader, marshal=true) sz = reader.read(4) sz = sz.unpack('N')[0] data = reader.read(sz) return data unless marshal begin Marshal.load(data) rescue DRb::DRbUnknown.new($!, data) end end
  97. צҧ͍ ❌ readable ❌ writable

  98. ੲΑ͘ݟͨόά select()

  99. σϞʢ͸͋Γ·ͤΜʣ sample ... ...

  100. tiny_drb.rb irb -r drb irb -r drb tiny_drb.rb Rdv https://github.com/seki/bartender/tree/master/sample

    Skip!
  101. tiny_drbͷ͓΋͠Ζ͍ͱ͜Ζ block-ish nonblock I/O Fiber select Rdv

  102. ଞʹࢼ࡞ͨ͠΋ͷ Fiber Rinda::TupleSpace Fiber WEBrick FD Thread Skip!

  103. Threadͱͷ଴ͪ߹Θͤ Thread.value IO.pipe close class ThreadTask def initialize(*args, &block) left,

    @right = IO.pipe @task = Thread.new do begin block.call(*args) ensure left.close end end end def value if @right Bartender.wait_readable(@right) @right.close end @task.value ensure @right = nil end end Skip!
  104. Multiplexerͷ·ͱΊ vs Thread Fiber block-ish non-block I/O

  105. Fiberೖ໳ͷ·ͱΊ Fibonacci Enumerator Rdv SizedQueue, channel Multiplexer

  106. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ ࣌ؒ͋Δ͔ͳʁ ✓ ͓·͚

  107. ͓·͚ Fiber ... Skip?

  108. ࠓճͷྫ͸

  109. ᠘ Fiber Thread FiberError gem

  110. ͭ·Γ Fiber ࢖༻͢Δgemͷ࢓૊Έ͘Β͍Θ͔ͬͯͳ͍ͱ

  111. ϋϚΔྫ Fiber Twitter JSON.parse String JSON.parse

  112. ϋϚΓͦ͏ͳྫ RDBMS ...

  113. ͠͹Β͓͘଴͍ͪͩ͘͞ ✓ ͓ΘΓ

  114. ·ͱΊ Thread Fiber Fiber API I/O

  115. Process ↔ Thread ↔ Fiber Fiber Thread Process ͋ͳͨ͸ͳʹΛ࢖͏΂͖ʁ

  116. ϓϩάϥϛϯάErlangΑΓ -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun() ->

    loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end.
  117. serverͷϝΠϯϧʔϓ -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun() ->

    loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. server main loop
  118. client͸ࡶʹϒϩοΫ x -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun()

    -> loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. client API
  119. ΋͜͠Εਅࣅͯ͠Δͱ x -module(kvs). -export([start/0, store/2, lookup/1]). start() -> register(kvs, spawn(fun()

    -> loop() end)). store(Key, Value) -> rpc({store, Key, Value}). lookup(Key) -> rpc({lookup, Key}). rpc(Q) -> kvs ! {self(), Q}, receive {kvs, Reply} -> Reply end. loop() -> receive {From, {store, Key, Value}} -> put(Key, {ok, Value}), From ! {kvs, true}, loop(); {From, {lookup, Key}} -> From ! {kvs, get(Key)}, loop() end. serverͷओॲཧͰrpcΛ ݺΜͩΒݻ·Δ͔΋...
  120. 2छྨͷϝοηʔδΛѻ͏ 1. Receive 4. Do it 2. Send task1 3.

    Receive task1 5. Send receive 2種類のメッセージ
  121. ͋ͱͰ΍ΔϦετ 1. Receive 4. Do it 2. Send task1 3.

    Receive task1 5. Send 処理できないやつをあとでやる
  122. ͋ͱͰ΍Δ࡞ઓ... ࣗ෼ͷ౎߹ͷྑ͍࣌ʹ΍ΔΑ ΍Γ͔͚ͷ͕ऴΘ͔ͬͯΒ࣍ͷཁٻΛ΍Δ ࣗ෼͸͏·͘ߦ͘

  123. ૬ޓʹґଘ͢Δ෦෼͕͋ͬͨΒ... ݏͳ༧ײ͕͢Δ

  124. "͋ͱͰ΍Δ"Λ࣋ͬͨ··͓ޓ͍ʹreplyΛ଴ͬͪΌ͏ ݏͳ༧ײ͕͢Δ

  125. 20ੈل ػೳ෼ࢄ ͜ͷόά΍ͬͨ͜ͱ͋Δ

  126. Ӆ͞Εͨґଘ͕͋ͬͨ ࣌ʑɺΈΜͳ͕reply଴ͪʹͳΔ ୭΋ࢭ·ͬͯͳ͍͚ͲγεςϜ͸ࢭ·ͬͯΔ