Sobes.tech
Middle+

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.