我有以下模型:
class User < ActiveRecord::Base
def send_message(content)
MessagePoro.new(content).deliver!
end
def self.send_to_all(content)
threads = []
all.each do |user|
threads << Thread.new do
user.send_message(content)
end
end
threads.each(&:join)
end
end
MessagePoro 模型可以很简单,例如应用程序/模型/message_poro.rb:
class MessagePoro
def initialize(content)
# ...
end
def deliver!
# ...
end
end
现在,当我有例如100 个用户,我正在运行 User.send_to_all("test") 我是 有时得到那些错误:
RuntimeError: Circular dependency detected while autoloading constant MessagePoro
或者:
wrong number of arguments (1 for 0)
我想这一定是因为没有加载 MessagePoro 并且所有线程都尝试同时加载它,或者类似的东西。由于这些错误仅在某些时候发生,我很确定只有在存在“竞争条件”或与线程有关的情况下才会发生。我曾尝试在启动 Threads 之前初始化 MessagePoro,并且我玩过 eager_loading,但问题似乎仍然存在。
我还能尝试什么来缓解这个问题?
最佳答案
我最近在尝试使用放置在 [rails_root]/lib
中的额外自定义库时遇到了一个非常相似的问题。目录。
TL;博士:
您可以使用急切加载来解决此问题,因为这样可以确保所有常量/模块/类在任何实际代码运行之前都在内存中。然而,为了这个工作:
config.eager_load = true
在 Rails 配置中设置(在生产环境中默认设置)config.eager_load_paths
,而不是 config.autoload_paths
. 或者
您可以使用
require
或 require_dependency
(另一个 ActiveSupport 功能)以确保在 Rails 自动加载之前显式加载您需要的代码。更多信息
正如 digidigo 在他的回复中提到的,循环依赖错误来自
ActiveSupport::Dependencies
模块,或 Rails 自动加载器 更笼统地说。此代码为 不是线程安全的 ,因为它使用该类/模块变量来存储它正在加载的文件。如果两个线程最终同时自动加载相同的东西,其中一个线程可能会因为看到要在该类变量中加载的文件并引发“循环依赖”错误而被误导。我在使用(线程)Puma 网络服务器在生产模式下运行 Rails 时遇到了这个问题。我们在
lib
中添加了一个小型库。在我们的 Rails 根目录中,最初添加了 lib
至config.autoload_once_paths
.在开发中一切都很好,但在生产中(启用 config.eager_load
和 config.cache_classes
),我们偶尔会在几乎同时请求的情况下遇到这些相同的循环依赖问题。几个小时后的调试,当我围绕循环依赖单步执行 ActiveSupport 代码并看到不同线程在代码中的不同点启动时,我最终看到了在我眼前发生的非线程安全。第一个线程会将要加载的文件添加到 loading
数组,然后第二个线程会在那里找到它并引发循环依赖错误。结果是向
autoload_paths
添加了一些东西或 autoload_once_paths
也不意味着它会被急切的加载拾取。然而,情况恰恰相反——添加到 eager_load_paths
的路径如果 eager_load 被禁用,将考虑自动加载(参见 this article 了解更多信息)。我们切换到 eager_load_paths
并且到目前为止没有进一步的问题。有趣的是,就在 Rails 4 测试版之前,生产环境中默认禁用自动加载,这意味着像这样的问题会导致 100% 的时间发生硬故障,而不是 5% 的时间导致古怪的线程失败.然而,这在 4.0 beta 版本中被及时恢复了——你可以看到一些关于它的(热情的)讨论 here (包括选择短语“老实说,你是在告诉我自己去他妈的?”)。不过,从那时起,该恢复已在 Rails 5.0.0beta1 之前恢复,因此希望将来有更少的人需要再次处理这个令人头疼的问题。
额外说明:
Rails 自动加载器与 Ruby 自动加载器完全分离——这似乎是因为 Rails 在尝试自动加载常量时对目录结构进行了更多推断。
自 Ruby 2.0 起,Ruby 的自动加载似乎已成为线程安全的,但这与 Rails 自动加载代码无关。 Rails 的自动加载器似乎肯定是 不是 线程安全,如前所述。
关于multithreading - Rails 4.2自动加载不是线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29439323/