Rails类方法是线程安全的吗?

时间:2018-12-18 01:22:29

标签: ruby-on-rails ruby-on-rails-5 puma

我在课堂上有这个方法:

class User < ApplicationRecord
  ...

  def answers
    @answers ||= HTTParty.get("http://www.example.com/api/users/#{self.id}/answers.json")
  end

  ...
end

由于我将Puma用作Web服务器,所以我想知道此代码是否线程安全?有人可以确认它,并在可能的情况下解释为什么这是线程安全的吗?

1 个答案:

答案 0 :(得分:2)

这是实例方法,不要与类方法混淆。 answers方法位于User instance 上,而不是User类本身。此方法在User实例上缓存答案,但是只要每个Web请求(例如UserUser.find()都实例化此User.find_by()实例,就可以),您就可以了,因为实例不在线程之间。通常的做法是在控制器中查找记录每个Web请求的记录,因此您很可能这样做。

如果此方法直接在User类上(例如User.answers),则需要评估在线程和Web请求之间维护该缓存值是否安全。< / p>

总而言之,您对线程安全性的唯一关注是类方法,类变量(使用两个在@@answers等符号处的实例变量)以及实例驻留在单个Web请求上的实例方法。

如果发现自己需要安全地使用类级变量,则可以使用Thread.current,它实际上是可以存储值的按线程哈希(如{})。例如{ {1}}就是一个例子。设置Thread.current[:foo] = 1时,ActiveSupport会使用它。

或者,您可能会发现需要一个可以安全地在线程间共享的单个数组的情况,在这种情况下,您需要查看Time.zone,这基本上可以让您拥有一个锁定的数组并解锁可让线程安全地对其进行读写操作。例如,Sidekiq gem使用Mutex来管理工人。您锁定互斥锁,以便其他任何人都无法对其进行更改,然后对其进行写入,然后对其进行解锁。重要的是要注意,如果任何其他线程想要在互斥锁处于锁定状态时写入互斥锁,则必须等待它被解锁(例如,该线程只是在其他线程写入时暂停),因此锁定为尽可能短。