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

Rails class method is thread safe?

我在 class:

中有这个方法
class User < ApplicationRecord
  ...

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

  ...
end

由于我使用 Puma 作为网络服务器,我想知道这段代码是否是线程安全的?有人可以确认一下吗?如果可能的话,解释一下为什么这是线程安全的?

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

如果此方法直接在 User class 上(例如 User.answers),那么您需要评估在整个过程中维护该缓存值是否安全线程和网络请求。

回顾一下,您唯一关心的线程安全是 class 方法、class 变量(使用两个 at 符号的实例变量,例如 @@answers)和其中的实例方法该实例依赖于过去的单个 Web 请求。

如果您发现自己需要安全地使用 class 级变量,您可以使用 Thread.current,它本质上是一个可以存储的每线程哈希(如 {})中的值。例如 Thread.current[:foo] = 1 就是一个例子。 ActiveSupport 在设置 Time.zone.

时使用它

或者你可能会发现有时你需要一个数组,你需要在线程之间安全地共享,在这种情况下你需要查看 Mutex,它基本上让你有一个数组,你lock 和 unlock 使线程可以安全地访问其中的读写。例如,Sidekiq gem 使用 Mutex 来管理 worker。您锁定 Mutex,以便其他人无法更改它,然后您写入它,然后解锁它。重要的是要注意,如果任何其他线程想要在锁定时写入 Mutex,则必须等待它解锁(例如,该线程在其他线程写入时暂停),因此锁定为尽可能短。