Sobes.tech
Middle+

Come viene implementato il multitasking preemptive in Ruby?

sobes.tech AI

Risposta dell'AI

In Ruby "puro" (MRI, JRuby, TruffleRuby), non esiste una vera multitasking preemptive per l'esecuzione parallela del codice in un singolo processo a causa del Global Interpreter Lock (GIL). Il GIL permette a un solo thread di eseguire codice Ruby in un dato momento.

Tuttavia, la multitasking preemptive in Ruby esiste a livello di input/output. Quando un thread esegue un'operazione di input/output bloccante (ad esempio, leggere un file, fare una richiesta di rete), il GIL viene rilasciato, consentendo a un altro thread di eseguire codice Ruby. Questo permette di ottenere parallelismo per le operazioni di input/output, ma non per i compiti CPU-bound.

Esistono implementazioni alternative di Ruby (ad esempio, Rubinius) che hanno tentato di implementare una vera multitasking senza GIL, ma non hanno avuto una diffusione così ampia come MRI.

Per l'esecuzione parallela di compiti CPU-bound in Ruby, si usano generalmente:

  • Processi (fork): Ogni processo ha il suo GIL, che permette di eseguire codice Ruby in parallelo. La comunicazione tra processi richiede meccanismi espliciti (IPC).
  • Librerie esterne e estensioni in C: Operazioni implementate in C possono essere eseguite fuori dal controllo del GIL, garantendo l'esecuzione parallela.

In Ruby 3.0 è stato introdotto il Scheduler di Fibers, che permette di eseguire operazioni di input/output in modo asincrono usando fibers e chiamate non bloccanti. Questo migliora le prestazioni per i compiti I/O-bound, ma non rende i compiti CPU-bound realmente paralleli in un singolo processo.

# Esempio di utilizzo di thread in MRI per compiti I/O-bound
require 'net/http'
require 'uri'

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

threads.each(&:join)

Pertanto, anche se Ruby ha mezzi per lavorare con i thread, la vera multitasking preemptive per i compiti legati alla CPU si ottiene generalmente tramite l'uso di più processi o implementazioni specifiche di Ruby senza GIL.