5

假设我的 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或线程同步吗?例如:

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
4

2 回答 2

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为假,则设置@objectsbegin/end表达式的结果

上下文可能会在这些步骤之间切换。上下文当然有可能在步骤 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
于 2012-05-07T05:25:25.260 回答
6

我来一刀。

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

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

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

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

于 2012-05-01T04:40:28.613 回答