Sobes.tech
Middle+

¿Cómo se implementa la multitarea preemptiva en Ruby?

sobes.tech AI

Respuesta de la IA

En Ruby "puro" (MRI, JRuby, TruffleRuby) no existe una verdadera multitarea preemptiva para la ejecución paralela de código en un solo proceso debido al Global Interpreter Lock (GIL). El GIL permite que solo un hilo ejecute código Ruby en un momento dado.

Sin embargo, la multitarea preemptiva en Ruby existe a nivel de entrada/salida. Cuando un hilo realiza una operación de entrada/salida bloqueante (por ejemplo, leer un archivo, realizar una solicitud de red), el GIL se libera, permitiendo que otro hilo ejecute código Ruby. Esto permite lograr paralelismo en operaciones de entrada/salida, pero no en tareas CPU-bound.

Existen implementaciones alternativas de Ruby (por ejemplo, Rubinius) que intentaron implementar una verdadera multitarea sin GIL, pero no tuvieron tanta difusión como MRI.

Para la ejecución paralela de tareas CPU-bound en Ruby, generalmente se utilizan:

  • Procesos (fork): Cada proceso tiene su propio GIL, lo que permite ejecutar código Ruby en paralelo. La comunicación entre procesos requiere mecanismos explícitos (IPC).
  • Bibliotecas externas y extensiones en C: Operaciones implementadas en C pueden ejecutarse fuera del control del GIL, asegurando ejecución paralela.

En Ruby 3.0 se introdujo el Programador de Fibers, que permite realizar operaciones de entrada/salida de forma asíncrona usando fibers y llamadas no bloqueantes. Esto mejora el rendimiento en tareas I/O-bound, pero no hace que las tareas CPU-bound sean verdaderamente paralelas en un solo proceso.

# Ejemplo de uso de hilos en MRI para tareas I/O-bound
require 'net/http'
require 'uri'

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

hilos.each(&:join)

Por lo tanto, aunque Ruby tiene mecanismos para trabajar con hilos, la verdadera multitarea preemptiva para tareas relacionadas con CPU generalmente se logra mediante el uso de múltiples procesos o implementaciones específicas de Ruby sin GIL.