2012-08-17 18 views
10

Ich muss eine Threads-Rails-App schreiben, weil ich sie auf Neo4j.rb betreibe, die eine Neo4j-Graph-Datenbank in den Rails-Prozess einbettet, und daher muss ich mehrere Anfragen von demselben Prozess bedienen. Ja, es wäre cool, wenn die Verbindung zu einer Neo4j-Datenbank wie SQL-Datenbanken funktioniert, aber das tut es nicht, also werde ich aufhören, mich zu beschweren und es einfach zu benutzen.Thread-sichere Rails-Controller-Aktionen - Instanzvariablen setzen?

Ich bin ziemlich besorgt über die Implikationen des Schreibens von gleichzeitigem Code (wie ich sollte), und brauche nur ein paar Ratschläge, wie man ein gemeinsames Szenario behandelt - ein Controller setzt eine Instanzvariable oder eine Variable im Session-Hash dann passiert etwas. Betrachten Sie den folgenden groben Code, um zu demonstrieren, was ich meine:

# THIS IS NOT REAL PRODUCTION CODE 
# I don't do this in real life, it is just to help me ask my question, I 
# know about one-way hashing, etc.! 

class SessionsController 
    def create 
    user = User.find_by_email_and_password(params[:email], params[:password]) 
    raise 'auth error' unless user 
    session[:current_user_id] = user.id 
    redirect_to :controller => 'current_user', :action => 'show' 
    end 
end 

class CurrentUserController 
    def show 
    @current_user = User.find(session[:current_user_id]) 
    render :action => :show # .html.erb file that uses @current_user 
    end 
end 

Die Frage: Gibt es Race-Bedingungen in diesem Code?

In SessionsController, sind die session Hash und die params Hash-Thread-local? Nehmen wir an, dass dieselbe Browsersitzung mehrere Anforderungen an/sessions # create (um die Routing-Syntax von Rails auszuleihen) mit unterschiedlichen Anmeldeinformationen ausführt, sollte der angemeldete Benutzer die Anfrage sein, die zuletzt die Zeile session[:current_user_id] = user.id erreicht hat? Oder sollte ich eine Mutex-Sperre um die Controller-Aktion wickeln?

Wird im CurrentUserController, wenn die show-Aktion gleichzeitig von zwei Anfragen mit unterschiedlichen Sitzungen ausgelöst wird, dieselbe @ current_user-Variable von beiden gesetzt? I.e. Wird die erste Anfrage, da sie die Datei .html.erb verarbeitet, feststellen, dass die @ accident_user Instanzvariable plötzlich vom zweiten Thread geändert wurde?

Dank

Antwort

10

Jede Anfrage erhält eine new instance of your controller. Folglich sind Controller-Instanzvariablen Thread-sicher. params und session werden auch von Controller-Instanzvariablen (oder dem Anforderungsobjekt selbst) unterstützt und sind daher ebenfalls sicher.

+0

Dies ist ein alter Thread, sorry. Aber ich habe eine Follow-up. Das macht sehr viel Sinn (und warum Leute sich nicht um Variablen in Controller-Methoden sorgen. Aber wenn das stimmt, bin ich verwirrt darüber, wie dieser Typ alle Probleme mit seinem Zähler hatte. Http://tenderlovemaking.com/2012/ 06/18/removing-config-threadsafe.html –

+1

@OliverShaw in diesem Post-Counter ist eine Klasse-Instanz-Variable (weil es in der 'class << self 'Block), so dass alle Anfragen würden den gleichen Zähler –

+0

kommen ändern zurück zu Ruby. Ich erinnerte mich an Klassenvariablen, aber nicht an Klasseninstanzvariablen. Danke! –

2

Es ist wichtig zu wissen, was zwischen Threads geteilt wird und was nicht.

Jetzt zurück zu Ihrem spezifischen Beispiel. Zwei Anfragen treffen gleichzeitig auf CurrentUserController#show, daher werden sie von zwei gleichzeitigen Threads behandelt. Der Schlüssel hier ist, dass jeder Thread seine eigene Instanz von CurrentUserController hat, so gibt es zwei @current_user Variablen, die nicht stören. Es gibt also keine Wettlaufbedingung um @current_user.

class ApplicationController < ActionController::Base 
    before_each :set_current_user 
    cattr_accessor :current_user 

    def set_current_user 
    self.class.current_user = User.find_by_id(session[:current_user_id]) 
    end 
end 

# model 
class LogMessage < ActiveRecord::Base 
    belongs_to :user 

    def self.log_action(attrs) 
    log_message = new(attrs) 
    log_message.user = ApplicationController.current_user 
    log_message.save 
    end 
end 

Auf Ganz allgemein, wegen GIL (Global Interpreter Lock) profitiert vom Einsatz Fäden in MRI Rubin sind eher begrenzt:

Ein Beispiel für Race-Bedingung sein würde. Es gibt Implementierungen, die frei von GIL (jruby) sind.

+0

Danke für das Beispiel, aber ich weiß bereits, dass Klassenvariablen ein Wettrennen auslösen würden. Bei der GIL-Ausgabe glaube ich, dass du falsch liegst - Ruby 1.9 Threads sind nativ, und ich benutze sowieso JRuby –

+1

Ich bin froh, dass Sie auf dem richtigen Weg sind, wie für MRI Ruby 1.9: Es hat native Threads und GIL, das ist globaler Mutex, der von diesen nativen Threads verwendet wird. –

+0

Ah, ich verstehe. Nun, wie gesagt, glücklicherweise benutze ich JRuby :) –