2012-08-17 30 views
10

我必須編寫一個線程Rails應用程序,因爲我在Neo4j.rb之上運行它,它在Rails進程中嵌入了一個Neo4j圖形數據庫,因此我必須提供來自同一進程的多個請求。是的,如果連接到像SQL數據庫那樣工作的Neo4j數據庫會很酷,但它不會,所以我會放棄抱怨並使用它。線程安全的Rails控制器操作 - 設置實例變量?

我很擔心編寫併發代碼的影響(我應該是這樣),並且只需要一些關於如何處理常見的常見場景的建議 - 控制器在會話散列中設置實例變量或變量,然後發生一些事情。請看下面的原油代碼來證明我的意思:

# 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 

的問題:有沒有在此代碼的任何競爭條件?

在SessionsController中,是session哈希和params哈希線程本地?假設同一個瀏覽器會話使用不同的憑證向/ sessions#create(要借用Rails路由語法)發出多個請求,那麼登錄的用戶應該是最後一行到達session[:current_user_id] = user.id的請求?或者我應該圍繞控制器操作包裝一個互斥鎖?

在CurrentUserController中,如果show動作被兩個具有不同會話的請求同時命中,相同的@current_user變量是由兩者設置的嗎?即第一個請求會在處理.html.erb文件時發現它的@current_user實例變量突然被第二個線程改變了嗎?

感謝

回答

10

每個請求都得到了new instance of your controller。因此控制器實例變量是線程安全的。 paramssession也支持控制器實例變量(或請求對象本身),所以也是安全的。

+0

這是一個古老的線程,抱歉。但我有一個後續。這很有意義(以及爲什麼人們不會在控制器方法中擔心@variables,但如果這是真的,我很困惑這傢伙每個人的計數器都有問題http://tenderlovemaking.com/2012/ 06/18/removed-config-threadsafe.html – 2017-02-12 13:19:27

+1

@OliverShaw在該計數器中是一個類實例變量(因爲它在'class << self'塊中),所以所有的請求都會修改同一個計數器 – 2017-02-12 13:41:32

+0

即將到來回到Ruby。我記得類變量,但沒有類實例變量。謝謝! – 2017-02-12 14:20:42

2

知道什麼是線程之間共享什麼是非常重要的。

現在回到您的具體示例。兩個請求同時觸發CurrentUserController#show,因此它們由兩個併發線程處理。這裏的關鍵是每個線程都有自己的實例CurrentUserController,所以有兩個不會干擾的變量@current_user。所以在@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 

在更一般的筆記,因爲GIL(全局解釋器鎖)的使用在MRI紅寶石線程的好處是相當有限的。有沒有GIL(jruby)的實現。

+0

感謝這個例子,但是我已經知道類變量會導致一場競賽。在GIL問題上,我認爲你錯了--Ruby 1.9線程是本地的,我使用JRuby。 – 2012-08-17 14:02:16

+1

我很高興你在正確的道路上。至於MRI紅寶石1。9:它有本地線程和GIL,這是本地線程使用的全局互斥鎖。 – 2012-08-17 14:26:54

+0

啊,我明白了。那麼,正如我所說的,幸好我使用JRuby :) – 2012-08-17 18:00:44