蒼時弦也
資深軟體工程師
發表於
淺談 Ruby 的 Fiber(二)
第一篇我們已經大致上了解 Fiber 的運作原理,不過要能夠實際上的掌握跟應用,我認為是需要靠實作來熟悉的。
所以,這一篇我們先來講學習 Socket 最常見的 TCP 伺服器實作吧!
為了要比較 Fiber 和 Thread 版,這次我們會先用 TCPServer 來實做一個簡易的 TCP 伺服器,並且用來比較兩者的差異。
TCPServer
首先,我們先用 Ruby 建立一個可以接收連線的 TCP 伺服器。
1require 'socket'
2
3server = TCPServer.new 3000
4loop do
5 client = server.accept
6 client.puts "HELLO WORLD"
7 client.close
8end
我們可以用 telnet 指令來做簡單的測試。
1telnet localhost 3000
目前的版本在連上之後就會馬上顯示 HELLO WORLD
然後關閉連線。
Blocking I/O
那麼,我們希望接收來自使用者的訊息,會像這樣修改。
1require 'socket'
2
3server = TCPServer.new 3000
4loop do
5 client = server.accept
6 client.puts "HELLO WORLD"
7 puts client.gets
8 client.close
9end
這個時候,如過我們用像這樣的順序進行操作,就會發現無法正常運作。
1# 視窗一
2telnet localhost 3000 # => 顯示 HELLO WORLD
3# 視窗二
4telnet localhost 3000 # => 沒有顯示
這是因為在 client.gets
的時候發生了 Blocking I/O(I/O 阻塞)的情況,也就是操作因為 #gets
嘗試讀取,但是因為讀取不到而阻止接下來的程式執行。
Thread
在這個時候,我們可以透過幾種方式解決。
- Process
- Thread
- Fiber
第一個方案因為是記憶體完全獨立的,所以我們就無法知道其他連線的用戶存在,所以一般來說不會使用。而 Thread 則是把 Process 切割後,遇到了一些情境像是 sleep
和 Blocking I/O
等情況,就會先把執行權轉交給其他 Thread 繼續執行。
所以,我們可以將程式修改成這樣。
1require 'socket'
2
3server = TCPServer.new 3000
4loop do
5 client = server.accept
6 client.puts "HELLO WORLD"
7 Thread.new do
8 puts client.gets
9 client.close
10 end
11end
如此一來,當我們碰到 Blocking I/O(#gets
)的情況,就會先將目前佔用的 Thread 轉交給其他可以繼續執行的 Thread 身上,先執行任務。
小結
這篇文章簡單的介紹了 Thread 的使用方式,以及該如何避免遇到 Blocking I/O 的處理方式,下一篇就來看看透過 Fiber 的流程控制機制,是怎樣迴避這個問題的。