variable rails programacion privados poo polimorfismo orientado objetos multiple instancia herencia con clases atributos ruby multithreading thread-safety metaprogramming ruby-1.9.2

programacion - ruby on rails poo



Seguridad de subprocesos: Variables de clase en Ruby (3)

Realizar escrituras / lecturas en variables de clase en Ruby no es seguro para subprocesos. La realización de escrituras / lecturas en variables de instancia parece ser segura para subprocesos. Dicho esto, ¿es seguro para los subprocesos realizar escrituras / lecturas en variables de instancia de una clase o un objeto de metaclase?

¿Cuáles son las diferencias entre estos tres ejemplos (ideados) en términos de seguridad de subprocesos?

EJEMPLO 1: EXCLUSIÓN MUTUA

class BestUser # (singleton class) @@instance_lock = Mutex.new # Memoize instance def self.instance @@instance_lock.synchronize do @@instance ||= best end end end

EJEMPLO 2: INSTANCIA ALMACENAMIENTO VARIABLE

class BestUser # (singleton class) # Memoize instance def self.instance @instance ||= best end end

EJEMPLO 3: INSTANCE VARIABLE STORAGE EN METACLASS

class BestUser # (singleton class) # Memoize instance class << self def instance @instance ||= best end end end


Los ejemplos 2 y 3 son exactamente iguales. No son en absoluto la seguridad de los hilos.

Por favor mira el ejemplo Abajo.

class Foo def self.bar @bar ||= create_no end def self.create_no no = rand(10000) sleep 1 no end end 10.times.map do Thread.new do puts "bar is #{Foo.bar}" end end.each(&:join)

Su resultado no es el mismo. El resultado es el mismo cuando se usa mutex como se muestra a continuación.

class Foo @mutex = Mutex.new def self.bar @mutex.synchronize { @bar ||= create_no } end def self.create_no no = rand(10000) sleep 1 no end end 10.times.map do Thread.new do puts "bar is #{Foo.bar}" end end.each(&:join)

Se ejecuta en CRuby 2.3.0.


Los ejemplos 2 y 3 son exactamente lo mismo. Los módulos y las clases también son objetos, y la definición de un método singleton en un objeto en realidad lo define en su clase singleton.

Dicho esto, y como ya ha establecido que la variable de acceso de instancia es segura para subprocesos, los ejemplos 2 y 3 son seguros para subprocesos. El ejemplo 1 también debería ser seguro para subprocesos, pero es inferior a los otros dos porque requiere la sincronización manual de variables.

Sin embargo, si necesita aprovechar el hecho de que las variables de clase se comparten dentro del árbol de herencia, es posible que deba usar el primer enfoque.

La seguridad de subprocesos inherente del lenguaje Ruby depende de la implementación.

MRI, antes de 1.9, implementó subprocesos en el nivel de VM . Esto significa que, aunque Ruby es capaz de programar la ejecución del código, en realidad no se ejecuta nada en paralelo dentro de un único proceso de Ruby. Ruby 1.9 utiliza subprocesos nativos sincronizados con un bloqueo de intérprete global . Solo el contexto que contiene el bloqueo puede ejecutar código.

n, x = 10, 0 n.times do Thread.new do n.times do x += 1 end end end sleep 1 puts x # 100

El valor de x es siempre consistente en MRI. En JRuby, sin embargo, la imagen cambia. Múltiples ejecuciones del mismo algoritmo rindieron los valores 76 , 87 , 98 , 88 , 94 . El resultado podría ser cualquier cosa porque JRuby utiliza subprocesos de Java, que son subprocesos reales y se ejecutan en paralelo.

Al igual que en el lenguaje Java, se requiere la sincronización manual para poder usar los hilos de forma segura en JRuby. El siguiente código siempre da como resultado valores consistentes para x :

require ''thread'' n, x, mutex = 10, 0, Mutex.new n.times do Thread.new do n.times do mutex.synchronize do x += 1 end end end end sleep 1 puts x # 100


Las variables de instancia no son seguras para subprocesos (y las variables de clase son aún menos seguras para subprocesos)

Los ejemplos 2 y 3, ambos con variables de instancia, son equivalentes, y NO son seguros para subprocesos, como se indica en @VincentXie. Sin embargo, aquí hay un mejor ejemplo para demostrar por qué no lo son:

class Foo def self.bar(message) @bar ||= message end end t1 = Thread.new do puts "bar is #{Foo.bar(''thread1'')}" end t2 = Thread.new do puts "bar is #{Foo.bar(''thread2'')}" end sleep 2 t1.join t2.join => bar is thread1 => bar is thread1

Debido a que la variable de instancia se comparte entre todos los subprocesos, como @VincentXie declaró en su comentario.

PS: Las variables de instancia a veces se denominan "variables de instancia de clase", según el contexto en el que se utilizan:

Cuando self es una clase, son variables de instancia de clases (variables de instancia de clase). Cuando self es un objeto, son variables de instancia de objetos (variables de instancia). - La respuesta de WindorC a una pregunta sobre esto.