Web 應用大多是 IO 密集型的,利用 Ruby 多進程+多線程模型將能大幅提升系統吞吐量。其原因在于:當Ruby 某個線程處于 IO Block 狀態時,其它的線程還可以繼續執行。但由于存在 Ruby GIL (Global Interpreter Lock),MRI Ruby 并不能真正利用多線程進行并行計算。JRuby 去除了 GIL,是真正意義的多線程,既能應付 IO Block,也能充分利用多核 CPU 加快整體運算速度。
上面說得比較抽象,下面就用例子一一加以說明。
Ruby 多線程和 IO Block
先看下面一段代碼(演示目的,沒有實際用途):
代碼如下:
# File: block_io1.rb
def func1
puts "sleep 3 seconds in func1/n"
sleep(3)
end
def func2
puts "sleep 2 seconds in func2/n"
sleep(2)
end
def func3
puts "sleep 5 seconds in func3/n"
sleep(5)
end
func1
func2
func3
代碼很簡單,3 個方法,用 sleep 模擬耗時的 IO 操作。 運行代碼(環境 MRI Ruby 1.9.3) 結果是:
代碼如下:
$ time ruby block_io1.rb
sleep 3 seconds in func1
sleep 2 seconds in func2
sleep 5 seconds in func3
real 0m11.681s
user 0m3.086s
sys 0m0.152s
比較慢,時間都耗在 sleep 上了,總共花了 10 多秒。
采用多線程的方式,改寫如下:
代碼如下:
# File: block_io2.rb
def func1
puts "sleep 3 seconds in func1/n"
sleep(3)
end
def func2
puts "sleep 2 seconds in func2/n"
sleep(2)
end
def func3
puts "sleep 5 seconds in func3/n"
sleep(5)
end
threads = []
threads << Thread.new { func1 }
threads << Thread.new { func2 }
threads << Thread.new { func3 }
threads.each { |t| t.join }
運行的結果是:
代碼如下:
$ time ruby block_io2.rb
sleep 3 seconds in func1
sleep 2 seconds in func2
sleep 5 seconds in func3
real 0m6.543s
user 0m3.169s
sys 0m0.147s
總共花了 6 秒多,明顯快了許多,只比最長的 sleep 5 秒多了一點。
上面的例子說明,Ruby 的多線程能夠應付 IO Block,當某個線程處于 IO Block 狀態時,其它的線程還可以繼續執行,從而使整體處理時間大幅縮短。
Ruby GIL 的影響
還是先看一段代碼(演示目的):
代碼如下:
# File: gil1.rb
require 'securerandom'
require 'zlib'
data = SecureRandom.hex(4096000)
16.times { Zlib::Deflate.deflate(data) }
代碼先隨機生成一些數據,然后對其進行壓縮,壓縮是非常耗 CPU 的,在我機器(雙核 CPU, MRI Ruby 1.9.3)運行結果如下:
代碼如下:
$ time ruby gil1.rb
real 0m8.572s
新聞熱點
疑難解答