thread run objetos objeto multihilos metodos hilos declaracion crear como clase ruby multithreading concurrency

ruby - run - ¿Rubí tiene multihilo real?



public void run (9)

Sé sobre la rosca "cooperativa" de rubí usando hilos verdes . ¿Cómo puedo crear subprocesos reales de "nivel de SO" en mi aplicación para hacer uso de múltiples cpu cores para el procesamiento?


¿Qué hay de usar drb ? No es un multihilo real sino una comunicación entre varios procesos, pero puede usarlo ahora en 1.8 y tiene una fricción bastante baja.



Dejaré que el "Monitor del sistema" responda a esta pregunta. Estoy ejecutando el mismo código (a continuación, que calcula los números primos) con 8 subprocesos de Ruby que se ejecutan en una máquina i7 (4 de núcleo hiperprocesado) en ambos casos ... la primera ejecución es con:

jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2014-02-03 6586) (OpenJDK 64-Bit Server VM 1.7.0_75) [amd64-java]

El segundo es con:

ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]

Curiosamente, la CPU es más alta para los subprocesos JRuby, pero el tiempo de finalización es un poco más corto para el Ruby interpretado. Es un poco difícil distinguirlo del gráfico, pero la segunda ejecución (interpretada de Ruby) usa aproximadamente la mitad de las CPU (¿sin hiperprocesamiento?)

def eratosthenes(n) nums = [nil, nil, *2..n] (2..Math.sqrt(n)).each do |i| (i**2..n).step(i){|m| nums[m] = nil} if nums[i] end nums.compact end MAX_PRIME=10000000 THREADS=8 threads = [] 1.upto(THREADS) do |num| puts "Starting thread #{num}" threads[num]=Thread.new { eratosthenes MAX_PRIME } end 1.upto(THREADS) do |num| threads[num].join end


Depende de la implementación:

  • RMI no tiene, YARV está más cerca.
  • JRuby y MacRuby tienen.




Ruby tiene closures como Blocks , lambdas y Procs . Para aprovechar al máximo los cierres y los múltiples núcleos en JRuby, los ejecutores de Java son útiles; Para MacRuby me gustan las colas de GCD .

Tenga en cuenta que el hecho de poder crear subprocesos reales de "nivel de SO" no implica que pueda usar varios cpu cores para el procesamiento paralelo. Mira los ejemplos a continuación.

Esta es la salida de un programa simple de Ruby que usa 3 hilos usando Ruby 2.1.0:

(jalcazar@mac ~)$ ps -M 69877 USER PID TT %CPU STAT PRI STIME UTIME COMMAND jalcazar 69877 s002 0.0 S 31T 0:00.01 0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb 69877 0.0 S 31T 0:00.01 0:00.00 69877 33.4 S 31T 0:00.01 0:08.73 69877 43.1 S 31T 0:00.01 0:08.73 69877 22.8 R 31T 0:00.01 0:08.65

Como puede ver aquí, hay cuatro subprocesos del sistema operativo, sin embargo, solo se está ejecutando el que tiene el estado R Esto se debe a una limitación en la forma en que se implementan los hilos de Ruby.


Mismo programa, ahora con JRuby. Puedes ver tres hilos con el estado R , lo que significa que se están ejecutando en paralelo.

(jalcazar@mac ~)$ ps -M 72286 USER PID TT %CPU STAT PRI STIME UTIME COMMAND jalcazar 72286 s002 0.0 S 31T 0:00.01 0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb 72286 0.0 S 31T 0:00.00 0:00.00 72286 0.0 S 33T 0:00.00 0:00.00 72286 0.0 S 31T 0:00.09 0:02.34 72286 7.9 S 31T 0:00.15 0:04.63 72286 0.0 S 31T 0:00.00 0:00.00 72286 0.0 S 31T 0:00.00 0:00.00 72286 0.0 S 31T 0:00.00 0:00.00 72286 0.0 S 31T 0:00.04 0:01.68 72286 0.0 S 31T 0:00.03 0:01.54 72286 0.0 S 31T 0:00.00 0:00.00 72286 0.0 S 31T 0:00.01 0:00.01 72286 0.0 S 31T 0:00.00 0:00.01 72286 0.0 S 31T 0:00.00 0:00.03 72286 74.2 R 31T 0:09.21 0:37.73 72286 72.4 R 31T 0:09.24 0:37.71 72286 74.7 R 31T 0:09.24 0:37.80


El mismo programa, ahora con MacRuby. También hay tres hilos que se ejecutan en paralelo. Esto se debe a que los subprocesos de MacRuby son subprocesos de POSIX ( subprocesos reales de "nivel de SO" ) y no hay GVL

(jalcazar@mac ~)$ ps -M 38293 USER PID TT %CPU STAT PRI STIME UTIME COMMAND jalcazar 38293 s002 0.0 R 0T 0:00.02 0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb 38293 0.0 S 33T 0:00.00 0:00.00 38293 100.0 R 31T 0:00.04 0:21.92 38293 100.0 R 31T 0:00.04 0:21.95 38293 100.0 R 31T 0:00.04 0:21.99


Una vez más, el mismo programa pero ahora con el buen viejo RMI. Debido al hecho de que esta implementación utiliza hilos verdes, solo se muestra un hilo

(jalcazar@mac ~)$ ps -M 70032 USER PID TT %CPU STAT PRI STIME UTIME COMMAND jalcazar 70032 s002 100.0 R 31T 0:00.08 0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb



Si está interesado en los subprocesos múltiples de Ruby, puede encontrar interesante mi informe de depuración de programas paralelos utilizando controladores de horquilla .
Para una descripción más general de las partes internas de Ruby, Ruby Under a Microscope es una buena lectura.
Además, Ruby Threads y el Global Intérprete Lock en C en Omniref explican en el código fuente por qué los hilos de Ruby no se ejecutan en paralelo.


Porque no se pudo editar esa respuesta, así que agregue una nueva respuesta aquí.

Actualización (2017-05-08)

Este artículo es muy antiguo y la información no sigue la banda de rodadura actual (2017). A continuación se incluye un suplemento:

  1. Opal es un compilador de fuente a fuente de Ruby a JavaScript. También tiene una implementación de Ruby corelib, que actualmente está muy activa, y existe una gran cantidad de (frontend) framework trabajado en él. y listo para la producción. Debido a que se basa en javascript, no admite subprocesos paralelos.

  2. truffleruby es una implementación de alto rendimiento del lenguaje de programación Ruby. Construido en GraalVM por Oracle Labs, TruffleRuby es una bifurcación de JRuby, lo combina con el código del proyecto Rubinius y también contiene el código de la implementación estándar de Ruby, MRI, desarrollo en vivo, no listo para producción. Esta versión ruby ​​parece nacer para el rendimiento, no sé si es compatible con hilos paralelos, pero creo que debería.


Ruby 1.8 solo tiene subprocesos verdes, no hay forma de crear un subproceso real de "nivel de SO". Sin embargo, ruby ​​1.9 tendrá una nueva característica llamada fibras, que te permitirá crear subprocesos reales a nivel del sistema operativo. Desafortunadamente, Ruby 1.9 todavía está en fase beta, está programado para mantenerse estable en un par de meses.

Otra alternativa es usar JRuby. JRuby implementa subprocesos como temas a nivel de sistema operativo, no hay "subprocesos verdes" en él. La última versión de JRuby es 1.1.4 y es equivalente a Ruby 1.8


Si está utilizando MRI, puede escribir el código de subprocesos en C como una extensión o utilizando la gema ruby-inline.


Si realmente necesita paralelismo en Ruby para un sistema de nivel de producción (donde no puede emplear una versión beta), los procesos son probablemente una mejor alternativa.
Pero, definitivamente vale la pena probar hilos bajo JRuby primero.

Además, si está interesado en el futuro de subprocesos bajo Ruby, este article le puede resultar útil.


Actualizado con el comentario de septiembre de 2011 de Jörg.

Parece que estás confundiendo dos cosas muy diferentes aquí: el lenguaje de programación Ruby y el modelo de subprocesos específico de una implementación específica del lenguaje de programación Ruby. Actualmente hay alrededor de 11 implementaciones diferentes del lenguaje de programación Ruby, con modelos de subprocesos muy diferentes y únicos.

(Desafortunadamente, solo dos de esas 11 implementaciones están realmente listas para el uso de producción, pero para finales de año, ese número probablemente subirá a cuatro o cinco). ( Actualización : ahora es 5: MRI, JRuby, YARV (el intérprete para Ruby 1.9), Rubinius y IronRuby).

  1. La primera implementación en realidad no tiene un nombre, lo que lo hace bastante incómodo para referirse a ella y es realmente molesto y confuso. A menudo se lo conoce como "Ruby", que es aún más molesto y confuso que no tener nombre, porque conduce a una confusión sin fin entre las características del lenguaje de programación de Ruby y una implementación particular de Ruby.

    A veces también se le llama "MRI" (para "Implementación de Ruby de Matz"), CRuby o MatzRuby.

    MRI implementa Ruby Threads como Green Threads dentro de su intérprete . Desafortunadamente, no permite que esos subprocesos se programen en paralelo, solo pueden ejecutar un subproceso a la vez.

    Sin embargo, cualquier número de subprocesos C (Subprocesos POSIX, etc.) puede ejecutarse en paralelo con el Subproceso de Ruby, por lo que las Bibliotecas C externas o las Extensiones C de MRI que crean subprocesos propios pueden ejecutarse en paralelo.

  2. La segunda implementación es YARV (abreviatura de "Yet Another Ruby VM"). YARV implementa Ruby Threads como POSIX o Windows NT Threads , sin embargo, utiliza un Global Intérpreter Lock (GIL) para asegurar que solo un Ruby Thread se pueda programar a la vez.

    Al igual que la resonancia magnética, los subprocesos C pueden funcionar en paralelo a los subprocesos de Ruby.

    En el futuro, es posible que la GIL se pueda dividir en bloqueos más precisos, permitiendo así que cada vez más códigos se ejecuten en paralelo, pero eso está muy lejos, ni siquiera está planeado todavía.

  3. JRuby implementa Ruby Threads como Threads nativos , donde "Threads nativos" en el caso de JVM obviamente significa "JVM Threads". JRuby no les impone ningún bloqueo adicional. Por lo tanto, si esos subprocesos pueden ejecutarse en paralelo depende de la JVM: algunas JVM implementan subprocesos JVM como subprocesos del sistema operativo y algunos como subprocesos verdes. (Las JVM principales de Sun / Oracle utilizan exclusivamente hilos de sistema operativo desde JDK 1.3)

  4. XRuby también implementa Ruby Threads como JVM Threads . Actualización : XRuby está muerto.

  5. IronRuby implementa Ruby Threads como Threads nativos , donde "Threads nativos" en caso de que el CLR signifique "Hilos CLR". IronRuby no les impone ningún bloqueo adicional, por lo tanto, deberían ejecutarse en paralelo, siempre que su CLR lo admita.

  6. Ruby.NET también implementa Ruby Threads como CLR Threads . Actualización: Ruby.NET está muerto.

  7. Rubinius implementa Ruby Threads como Green Threads dentro de su Máquina Virtual . Más precisamente: la máquina virtual Rubinius exporta una construcción de concurrencia / paralelismo / flujo de control no local muy ligera y muy flexible, denominada " Task ", y todas las demás construcciones de concurrencia (Temas en esta discusión, pero también Continuations , Actors y otras cosas) ) se implementan en Ruby puro, utilizando Tareas.

    Sin embargo, Rubinius no puede (actualmente) programar subprocesos en paralelo, agregando que no es demasiado problema: Rubinius ya puede ejecutar varias instancias de VM en varios subprocesos POSIX en paralelo , dentro de un proceso de Rubinius. Dado que los subprocesos se implementan realmente en Ruby, pueden, como cualquier otro objeto Ruby, ser serializados y enviados a una máquina virtual diferente en un subproceso POSIX diferente. (Ese es el mismo modelo que el BEAM Erlang VM usa para la concurrencia SMP. Ya está implementado para los actores Rubinius ).

    Actualización : la información sobre Rubinius en esta respuesta es sobre la máquina virtual Shotgun, que ya no existe. El "nuevo" C ++ VM no usa subprocesos verdes programados en múltiples VM (es decir, el estilo Erlang / BEAM), usa un VM más tradicional con múltiples modelos de subprocesos nativos del sistema operativo, como el empleado por, por ejemplo, el CLR, Mono , y casi todas las JVM.

  8. MacRuby comenzó como un puerto de YARV en la parte superior de Objective-C Runtime y CoreFoundation and Cocoa Frameworks. Ahora se ha desviado significativamente de YARV, pero AFAIK actualmente todavía comparte el mismo Modelo de subprocesamiento con YARV . Actualización: MacRuby depende del recolector de basura de manzanas que se declara en desuso y se eliminará en versiones posteriores de MacOSX, MacRuby no está muerto.

  9. Cardinal es una implementación de Ruby para la máquina virtual Parrot . Aún no implementa subprocesos, sin embargo, cuando lo haga, probablemente los implementará como Parrot Threads . Actualización : Cardenal parece muy inactivo / muerto.

  10. MagLev es una implementación de Ruby para la máquina virtual de GemStone / S Smalltalk . No tengo información sobre qué modelo de subproceso utiliza GemStone / S, qué modelo de subproceso utiliza MagLev o incluso si los subprocesos están implementados aún (probablemente no).

  11. HotRuby no es una implementación completa de Ruby propia. Es una implementación de una máquina virtual de bytes de YARV en JavaScript. HotRuby no admite subprocesos (¿todavía?) Y cuando lo hace, no podrán ejecutarse en paralelo, porque JavaScript no admite el verdadero paralelismo. Sin embargo, hay una versión de ActionScript de HotRuby, y ActionScript podría admitir el paralelismo. Actualización : HotRuby está muerto.

Desafortunadamente, solo dos de estas 11 implementaciones de Ruby están listas para la producción: MRI y JRuby.

Por lo tanto, si desea verdaderos hilos paralelos, JRuby es actualmente su única opción, aunque no sea mala: JRuby es en realidad más rápido que MRI, y posiblemente más estable.

De lo contrario, la solución "clásica" de Ruby es utilizar procesos en lugar de hilos para el paralelismo. La Ruby Core Library contiene el módulo de Process con el método Process.fork , que facilita Process.fork el proceso de otro proceso de Ruby. Además, la biblioteca estándar de Ruby contiene la biblioteca distribuida de Ruby (dRuby / dRb) , que permite que el código de Ruby se distribuya de manera trivial en múltiples procesos, no solo en la misma máquina sino también en toda la red.