DEV Community

Vee Satayamas
Vee Satayamas

Posted on

ลอง event loop และ non-blocking IO ใน crystal-lang

หลายครั้งผมแค่อยากได้ Ruby ที่มันเร็ว ไม่ต้องแนว ไม่ต้อง functional ก็ได้ ไม่ต้อง minimal ก็ได้

Crystal เป็นภาษาโปรแกรมแบบ static type ความเร็วแบบ thread เดียว ผมเคยเอามาลองตัดคำ ถ้าไม่ optimize อะไรเลย ความเร็วไล่ ๆ กับ Go

ประเด็น hot ช่วงนี้คือถ้าใช้แบบ concurrent จะเป็นอย่างไร Crystal ไม่ได้ใช้ multithreading แน่ ๆ เพราะมันยังทำไม่เสร็จ 🤣 แต่ว่าตอนนี้มีพวก event loop และ fiber แน่ ๆ ดูคร่าว ๆ แล้วน่าจะทำงานคล้าย ๆ Node.js ได้โดยไม่ต้องเขียน async-await

code ต่อไปนี้ผมเขียนมาลองว่าจะเป็นแบบที่คิดจริงหรือไม่

require "socket"

def load(id, chan)
  puts "ID=#{id}; BEFORE"
  (id..11).each do 
    socket = TCPSocket.new("www.ku.ac.th", 80)
    puts "ID=#{id}; Blocking?=#{socket.blocking}"
    socket.close
  end
  puts "ID=#{id}; AFTER"
  chan.send nil
end

def main
  chan = Channel(Nil).new
  (1..10).each{|i| spawn(load(i,chan))}
  # Wait
  (1..10).each{chan.receive}
end

main 
Enter fullscreen mode Exit fullscreen mode

code มันก็จะคล้าย ๆ Ruby หน่อย ผมเขียนให้ ถ้า id ยิ่งน้อยยิ่งรันนาน เพื่อที่จะดูว่างานที่ spawn มาทีหลังมันมันแซงมาเสร็จก่อนได้เปล่า อีกอย่างก็คือ print มาให้ดูด้สนว่าใช้ non-blocking IO

ID=1; BEFORE
ID=2; BEFORE
ID=3; BEFORE
ID=4; BEFORE
ID=5; BEFORE
ID=6; BEFORE
ID=7; BEFORE
ID=8; BEFORE
ID=9; BEFORE
ID=10; BEFORE
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=3; Blocking?=false
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=6; Blocking?=false
ID=7; Blocking?=false
ID=8; Blocking?=false
ID=9; Blocking?=false
ID=10; Blocking?=false
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=3; Blocking?=false
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=7; Blocking?=false
ID=6; Blocking?=false
ID=8; Blocking?=false
ID=9; Blocking?=false
ID=2; Blocking?=false
ID=1; Blocking?=false
ID=10; Blocking?=false
ID=10; AFTER
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=3; Blocking?=false
ID=7; Blocking?=false
ID=6; Blocking?=false
ID=8; Blocking?=false
ID=9; Blocking?=false
ID=9; AFTER
ID=2; Blocking?=false
ID=1; Blocking?=false
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=3; Blocking?=false
ID=7; Blocking?=false
ID=6; Blocking?=false
ID=8; Blocking?=false
ID=8; AFTER
ID=2; Blocking?=false
ID=1; Blocking?=false
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=3; Blocking?=false
ID=7; Blocking?=false
ID=7; AFTER
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=6; Blocking?=false
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=3; Blocking?=false
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=6; Blocking?=false
ID=6; AFTER
ID=4; Blocking?=false
ID=5; Blocking?=false
ID=5; AFTER
ID=3; Blocking?=false
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=4; Blocking?=false
ID=4; AFTER
ID=3; Blocking?=false
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=3; Blocking?=false
ID=3; AFTER
ID=1; Blocking?=false
ID=2; Blocking?=false
ID=2; AFTER
ID=1; Blocking?=false
ID=1; AFTER
Enter fullscreen mode Exit fullscreen mode

อันนี้ก็จะเห็นได้เลยว่า ID=1 ที่เริ่มรันก่อนมันเสร็จทีหลังเลย สรุปก็คือใน Crystal ใช้ non-blocking IO กับ event loop ได้โดยที่ programmer ไม่ต้องทำอะไรพิเศษเลย ก็แค่เขียน ๆ ไปธรรมดา ในกรณีที่เป็น web server ส่วน spawn และ channel ก็ไม่ต้องเขียนเองด้วย

Go, Erlang, Elixir ทำอะไรได้มากกว่านี้ แต่ Crystal มันเป็นภาษาที่แทบไม่ต้องปรับตัวเลย สำหรับคนที่เคยเขียน Ruby งาน CPU bound ก็น่าจะเร็วกว่า Erlang แลพ Elixir ด้วย

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Retry later