Sobes.tech
Middle+

Kaip Ruby įgyvendina išankstinį daugiaprocesiškumą?

sobes.tech AI

Atsakymas iš AI

"Švarus" Ruby (MRI, JRuby, TruffleRuby) neturi tikros išstūmiančios daugiaprocesinės vykdymo galios viename procese dėl Global Interpreter Lock (GIL). GIL leidžia vykdyti tik vienam gijų Ruby kodą bet kuriuo metu:

Tačiau išstūmiančio daugiaprocesinio vykdymo galimybė Ruby yra įgyvendinta įvesties-išvesties lygyje. Kai gija vykdo blokuojančią įvestį-išvestį (pvz., failo skaitymą, tinklo užklausą), GIL atlaisvinama, leidžiant kitai gijai vykdyti Ruby kodą. Tai leidžia pasiekti paralelizmą įvesties-išvesties operacijoms, bet ne CPU ribotoms užduotims:

Yra alternatyvių Ruby įgyvendinimų (pvz., Rubinius), kurie bandė įgyvendinti tikrą daugiaprocesinį vykdymą be GIL, bet jie nesulaukė tokio plataus paplitimo kaip MRI:

Paraleliam CPU ribotų užduočių vykdymui Ruby dažniausiai naudojami:

  • Procesai (fork) — kiekvienas procesas turi savo GIL, leidžiantį vykdyti Ruby kodą paraleliai. Duomenų mainai tarp procesų reikalauja aiškių mechanizmų (IPC).
  • Išorinės bibliotekos ir C plėtiniai — C kalba įgyvendintos operacijos gali būti vykdomos už GIL kontrolės, užtikrinant paralelų vykdymą:

Ruby 3.0 buvo įvestas Fibers Scheduler, kuris leidžia asinchroniškai vykdyti įvesties-išvesties operacijas, naudojant fibres ir neblokuojančius iškvietimus. Tai pagerina našumą I/O ribotoms užduotims, bet nepadaro CPU ribotų užduočių tikru paralelizmu viename procese:

# Pavyzdys naudojant MRI gijas I/O ribotoms užduotims
require 'net/http'
require 'uri'

urls = %w[http://example.com http://ruby-lang.org]
threds = urls.map do |url|
  Thread.new do
    uri = URI.parse(url)
    response = Net::HTTP.get_response(uri)
    puts "Parsisiųsta #{url} (Statusas: #{response.code})"
  end
end

threads.each(&:join)

Taigi, nors Ruby turi įrankių darbui su gijomis, tikras išstūmiančios daugiaprocesinės vykdymo galios pasiekimas daugiausia pasiekiamas naudojant kelis procesus arba specialias Ruby realizacijas be GIL.