Rails中的类实例变量应该在互斥锁中设置吗?

时间:2012-04-30 15:50:42

标签: ruby-on-rails ruby ruby-on-rails-3 synchronization

假设我的Rails项目中有一个设置实例变量的Ruby类。

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

是否有可能多次设置@objects?是否有可能在一个请求期间,在上面的begin / end之间执行代码时,可以在第二个请求期间调用此方法?我想这实际上归结为Rails服务器实例如何分叉的问题。

我应该使用Mutex还是线程同步? 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

2 个答案:

答案 0 :(得分:7)

即使在MRI中,也可以(并且可取)以多线程模式运行Rails。这可以通过更改production.rb中的一行来完成。

config.threadsafe!

在MRI中,两个线程无法同时运行代码,但上下文切换可能随时发生。在Rubinius和JRuby中,线程可以同时运行代码。

让我们看看你展示的代码:

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

||=代码扩展为:

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

这意味着该过程实际上有两个步骤:

  1. 查找@objects
  2. 如果@objects是假的,请将@objects设置为begin/end表达式的结果
  3. 上下文可以在这些步骤之间切换。上下文当然可以在步骤2的中间切换。这意味着您可能会多次运行块而不是一次。在MRI中,这可能是可以接受的,但是在表达式周围锁定互斥体是非常直接的,所以就这样做。

    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
    

答案 1 :(得分:6)

我会刺伤。

Rails是单线程的。对Rails应用程序的连续请求要么排队,要么由单独的应用程序实例处理(读取:进程)。 @objects类中定义的类实例变量Something的值存在于进程的范围内,而不在应用程序的任何实例的范围内。

因此,互斥是不必要的,因为你永远不会遇到两个进程访问同一资源的情况,因为这两个进程的内存空间完全是分开的。

我认为这提出了另一个问题,@objects是一个共享资源,如果是这样,我认为它需要以不同的方式实现。

免责声明:我在这里可能完全不合适,事实上我希望我今天能学到一些东西:)