Hogyan valósul meg a Ruby-ban a preemptív multitasking?
sobes.tech MI
Válasz az MI-től
A "tiszta" Ruby (MRI, JRuby, TruffleRuby) esetében nincs valódi preemptív multitasking a párhuzamos kódvégrehajtásra egy folyamaton belül a Global Interpreter Lock (GIL) miatt. A GIL csak egy szál számára engedélyezi a Ruby-kód végrehajtását egy adott időben.
Azonban, a preemptív multitasking Ruby-ben az input/output szinten létezik. Amikor egy szál blokkoló bemenet/kimenet műveletet végez (például fájl olvasás, hálózati kérés), a GIL felszabadul, lehetővé téve egy másik szálnak Ruby-kód végrehajtását. Ez lehetővé teszi az I/O-alapú műveletek párhuzamos végrehajtását, de nem a CPU-kötött feladatok esetében.
Vannak alternatív Ruby-implementációk (például Rubinius), amelyek megpróbáltak valódi GIL nélküli multitaskot megvalósítani, de ezek nem terjedtek el olyan széles körben, mint az MRI.
A CPU-kötött feladatok párhuzamos végrehajtásához Ruby-ben általában:
- Folyamatok (
fork): Minden folyamatnak saját GIL-je van, ami lehetővé teszi a Ruby-kód párhuzamos végrehajtását. A folyamatok közötti adatcsere explicit mechanizmusokat (IPC) igényel. - Külső könyvtárak és C-kiterjesztések: A C-ben megvalósított műveletek a GIL ellenőrzésén kívül futtathatók, biztosítva a párhuzamos végrehajtást.
A Ruby 3.0-ban bevezették a Fibers Scheduler-t, amely lehetővé teszi az aszinkron bemenet/kiadás műveletek végrehajtását fiber-ek és nem blokkoló hívások segítségével. Ez javítja az I/O-kötött feladatok teljesítményét, de nem teszi valóban párhuzamossá a CPU-kötött feladatokat egy folyamaton belül.
# Példa a Ruby MRI-ben használt szálakra I/O-kötött feladatokhoz
require 'net/http'
require 'uri'
urls = %w[http://example.com http://ruby-lang.org]
szálak = urls.map do |url|
Thread.new do
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
puts "Letöltve #{url} (Állapot: #{response.code})"
end
end
szálak.each(&:join)
Tehát, bár Ruby rendelkezik szálakkal való munkára alkalmas eszközökkel, a CPU-kötött feladatok valódi preemptív multitaskingja általában több folyamat használatával vagy GIL nélküli speciális Ruby-implementációkkal érhető el.