Kuidas Ruby-s rakendatakse eelnevat multitaskingut?
sobes.tech AI
Vastus AI-lt
"Puhas" Ruby (MRI, JRuby, TruffleRuby) ei oma tõelist väljaajavat mitmeülesandevõimet ühe protsessi sees, kuna Global Interpreter Lock (GIL) võimaldab korraga täita ainult ühte lõime Ruby-koodi:
Kuid, väljaajav mitmeülesandevõime Ruby-s on olemas sisend-väljundi tasemel. Kui lõim täidab blokeerivat sisend-väljundi operatsiooni (näiteks faili lugemine, võrgupäring), siis GIL vabastatakse, võimaldades teisel lõimel Ruby-koodi täita. See võimaldab saavutada paralleelsust sisend-väljundi operatsioonide jaoks, kuid mitte CPU-põhiste ülesannete jaoks:
On olemas alternatiivseid Ruby teostusi (näiteks Rubinius), mis on püüdnud teostada tõelist väljaajavat mitmeülesandevõimet ilma GIL-ita, kuid need ei ole saavutanud nii laialdast levikut kui MRI:
CPU-põhiste ülesannete paralleelseks täitmiseks Ruby-s kasutatakse tavaliselt:
- Protsesse (
fork) — iga protsessil on oma GIL, mis võimaldab Ruby-koodi paralleelselt täita. Andmete vahetamine protsesside vahel nõuab spetsiifilisi mehhanisme (IPC). - Väliseid teeke ja C-l põhinevaid laiendusi — C keeles teostatud operatsioone saab täita väljaspool GIL-i kontrolli, tagades paralleelse täitmise:
Ruby 3.0-s on sisse viidud Fibers Scheduler, mis võimaldab asünkroonselt täita sisend-väljundi operatsioone, kasutades fibereid ja mitteblokeerivaid üleskutseid. See parandab I/O-põhiste ülesannete jõudlust, kuid ei tee CPU-põhiseid ülesandeid tõeliselt paralleelseteks ühes protsessis:
# Näide MRI lõimede kasutamisest I/O-põhiste ülesannete jaoks
require 'net/http'
require 'uri'
urls = %w[http://example.com http://ruby-lang.org]
lõimed = urls.map do |url|
Thread.new do
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
puts "Allalaaditud #{url} (Staatus: #{response.code})"
end
end
lõimed.each(&:join)
Seega, kuigi Ruby-l on tööriistad lõimede jaoks, saavutatakse tõeline väljaajav mitmeprotsessiline täitmine peamiselt mitme protsessi või spetsiaalsete GIL-vaba Ruby teostuste kasutamisega.