Sobes.tech
Middle+

Jak je v Ruby implementováno předběžné multitasking?

sobes.tech AI

Odpověď od AI

V "čistém" Ruby (MRI, JRuby, TruffleRuby) neexistuje skutečný předběžný preemptivní multitasking pro paralelní provádění kódu v jednom procesu kvůli Global Interpreter Lock (GIL). GIL umožňuje pouze jednomu vláknu vykonávat Ruby kód v daném okamžiku.

Nicméně, preemptivní multitasking v Ruby existuje na úrovni vstupu/výstupu. Když vlákno provádí blokující operaci vstupu/výstupu (například čtení souboru, síťový požadavek), GIL je uvolněn, což umožňuje jinému vláknu vykonávat Ruby kód. To umožňuje dosáhnout paralelismu u operací vstupu/výstupu, ale ne u CPU-bound úloh.

Existují alternativní implementace Ruby (například Rubinius), které se pokusily implementovat skutečný multitasking bez GIL, ale nedosáhly takové široké rozšířenosti jako MRI.

Pro paralelní provádění CPU-bound úloh v Ruby se obvykle používají:

  • Procesy (fork): Každý proces má svůj vlastní GIL, což umožňuje paralelní vykonávání Ruby kódu. Výmena dat mezi procesy vyžaduje explicitní mechanismy (IPC).
  • Externí knihovny a rozšíření v C: Operace implementované v C mohou být vykonávány mimo kontrolu GIL, čímž se zajišťuje paralelní provádění.

V Ruby 3.0 byl zaveden Fibers Scheduler, který umožňuje asynchronní provádění operací vstupu/výstupu pomocí fiberů a neblokujících volání. To zlepšuje výkon pro I/O-bound úlohy, ale nečiní CPU-bound úlohy skutečně paralelní v jednom procesu.

# Příklad použití vláken v MRI pro I/O-bound úlohy
require 'net/http'
require 'uri'

urls = %w[http://example.com http://ruby-lang.org]
vlákna = urls.map do |url|
  Thread.new do
    uri = URI.parse(url)
    response = Net::HTTP.get_response(uri)
    puts "Staženo #{url} (Stav: #{response.code})"
  end
end

vlákna.each(&:join)

Tedy, i když Ruby má prostředky pro práci s vlákny, skutečný preemptivní multitasking pro CPU-vázané úlohy je obvykle dosažen použitím více procesů nebo specifických Ruby implementací bez GIL.