假设我的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
答案 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
这意味着该过程实际上有两个步骤:
@objects
@objects
是假的,请将@objects
设置为begin/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
答案 1 :(得分:6)
我会刺伤。
Rails是单线程的。对Rails应用程序的连续请求要么排队,要么由单独的应用程序实例处理(读取:进程)。 @objects
类中定义的类实例变量Something
的值存在于进程的范围内,而不在应用程序的任何实例的范围内。
因此,互斥是不必要的,因为你永远不会遇到两个进程访问同一资源的情况,因为这两个进程的内存空间完全是分开的。
我认为这提出了另一个问题,@objects
是一个共享资源,如果是这样,我认为它需要以不同的方式实现。
免责声明:我在这里可能完全不合适,事实上我希望我今天能学到一些东西:)