Kako je u Rubyju implementirano preemptivno multitasking?
sobes.tech АИ
Одговор од АИ
U "čistom" Ruby (MRI, JRuby, TruffleRuby) ne postoji prava preemptivna multitasking za paralelno izvršavanje koda u jednom procesu zbog Global Interpreter Lock (GIL). GIL omogućava da samo jedan thread u datom trenutku izvršava Ruby kod.
Međutim, preemptivna multitasking u Ruby postoji na nivou ulaza/izlaza. Kada thread izvršava blokirajuću operaciju ulaza/izlaza (npr. čitanje fajla, mrežni zahtev), GIL se oslobađa, omogućavajući drugom thread-u da izvršava Ruby kod. Ovo omogućava postizanje paralelizma za operacije ulaza/izlaza, ali ne i za CPU-bound zadatke.
Postoje alternativne implementacije Ruby-ja (npr. Rubinius) koje su pokušale da implementiraju pravu multitaskingu bez GIL-a, ali nisu stekle široku rasprostranjenost kao MRI.
Za paralelno izvršavanje CPU-bound zadataka u Ruby-ju, obično se koriste:
- Procesi (
fork): Svaki proces ima svoj GIL, što omogućava paralelno izvršavanje Ruby koda. Razmena podataka između procesa zahteva eksplicitne mehanizme (IPC). - Spoljašnje biblioteke i C proširenja: Operacije implementirane u C mogu se izvršavati van kontrole GIL-a, obezbeđujući paralelno izvršavanje.
U Ruby 3.0 je uveden Fibers Scheduler, koji omogućava asinhrono izvršavanje operacija ulaza/izlaza koristeći fibers i neblokirajuće pozive. Ovo poboljšava performanse za I/O-bound zadatke, ali ne čini CPU-bound zadatke pravom paralelom u jednom procesu.
# Primer korišćenja niti u MRI za I/O-bound zadatke
require 'net/http'
require 'uri'
urls = %w[http://example.com http://ruby-lang.org]
niti = urls.map do |url|
Thread.new do
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
puts "Preuzeto #{url} (Status: #{response.code})"
end
end
niti.each(&:join)
Dakle, iako Ruby ima mehanizme za rad sa nitima, prava preemptivna multitaskinga za CPU-zavisne zadatke uglavnom se postiže korišćenjem više procesa ili specifičnih Ruby implementacija bez GIL.