2012-04-30 5 views
5

Digamos que tengo una clase de Ruby en mi proyecto Rails que establece una variable de instancia.¿Deben establecerse variables de instancia de clase en Rails dentro de un mutex?

class Something 
    def self.objects 
    @objects ||= begin 
     # some logic that builds an array, which is ultimately stored in @objects 
    end 
    end 
end 

¿Es posible que @objects podría fijarse varias veces? ¿Es posible que durante una solicitud, al ejecutar el código entre begin/end anterior, este método se pueda invocar durante una segunda solicitud? Esto realmente se reduce a una cuestión de cómo se bifurcan las instancias del servidor de Rails, supongo.

¿Debo utilizar una Mutex o una sincronización de subprocesos? e.g .:

class Something 
    def self.objects 
    return @objects if @objects 

    Thread.exclusive do 
     @objects ||= begin 
     # some logic that builds an array, which is ultimately stored in @objects 
     end 
    end 
    end 
end 
+0

a preguntar Yehuda pesando en –

Respuesta

6

Es posible (y deseable) para ejecutar Rails en un modo multi-hilo incluso en MRI Esto se puede lograr cambiando una línea en production.rb.

config.threadsafe! 

En MRI, dos subprocesos no pueden ejecutar código simultáneamente, pero un cambio de contexto puede ocurrir en cualquier momento. En Rubinius y JRuby, los hilos pueden ejecutar código simultáneamente. mirada

Vamos en el código que mostró:

class Something 
    def self.objects 
    @objects ||= begin 
     # some logic that builds an array, which is ultimately stored in @objects 
    end 
    end 
end 

El código ||= consigue ampliar a algo como:

class Something 
    def self.objects 
    @objects || (@objects = begin 
     # some logic that builds an array, which is ultimately stored in @objects 
    end) 
    end 
end 

Esto significa que en realidad hay dos pasos en el proceso:

  1. buscar @objects
  2. Si es
  3. @objects Falsy, establecer @objects a los resultados de la expresión begin/end

Puede ser posible que el contexto para cambiar entre estos pasos. Es posible que el contexto cambie en el medio del paso 2. Esto significa que puede terminar ejecutando el bloque varias veces en lugar de una vez. En MRI, esto puede ser aceptable, pero es perfectamente sencillo bloquear un mutex alrededor de la expresión, así que hazlo.

class Something 
    MUTEX = Mutex.new 

    def self.objects 
    MUTEX.synchronize do 
     @objects ||= begin 
     # some logic that builds an array, which is ultimately stored in @objects 
     end 
    end 
    end 
end 
+0

sería más eficiente hacer 'def self.objects; @objects || MUTEX.synchronize {@objects || = [...]}; end' - de esa manera no intenta obtener un bloqueo si la var ya se ha configurado? también, ¿podríamos usar un bloque genérico 'Thread.synchronize'? –

+0

err, 'Thread.exclusive' –

6

Haré una puñalada.

Los rieles son de rosca simple. Las solicitudes sucesivas a una aplicación de Rails son puestas en cola o manejadas por instancias de aplicación separadas (leer: procesos). El valor de la variable de instancia de clase @objects definida en su clase Something existe dentro del alcance del proceso, no dentro del alcance de ninguna instancia de su aplicación.

Por lo tanto, un mutex sería innecesario ya que nunca se encontraría con el caso donde dos procesos acceden al mismo recurso porque los espacios de memoria de los dos procesos están completamente separados.

Creo que esto plantea otra pregunta, es @objects destinado a ser un recurso compartido, si es así, creo que debe implementarse de manera diferente.

responsabilidad: Puedo estar completamente fuera de aquí, de hecho, en cierto modo me espero estar para que pueda aprender algo hoy :)

+0

... no estoy seguro tampoco, pero estoy bastante seguro de que son correctos. :) – Phrogz

+1

De hecho, creo que estás muy cerca de lugar. La advertencia que encuentro con tu respuesta radica en qué intérprete está en uso. JRuby, por ejemplo, puede tener estos problemas. Como tiene soporte de subprocesos real, cada proceso no es solo un tenedor del padre. Entonces, para MRI, creo que tienes razón. –

+0

Debería haber mencionado que estaba centrado principalmente en la resonancia magnética con mi pregunta. Básicamente estaba bajo la misma impresión que lo que describiste en tu respuesta, pero pensé que valía la pena verificarlo. '@objetos' no pretende ser un recurso compartido, sino solo un caché para evitar búsquedas repetidas de un proceso que consume tiempo. ¡Gracias! –

Cuestiones relacionadas