【问题标题】:Are rails controllers multithreaded? Thread.exclusive in controllersrails 控制器是多线程的吗?控制器中的 Thread.exclusive
【发布时间】:2009-09-07 11:42:33
【问题描述】:

Rails 控制器是多线程的吗?

如果是这样,我是否可以通过简单的操作来保护某段代码(每十分钟触发一次)不被多个线程运行

require 'thread'
Thread.exclusive do
     # stuff here
end

我需要以某种方式在监视器上同步吗?

【问题讨论】:

    标签: ruby-on-rails ruby multithreading


    【解决方案1】:

    在基本的 Rails 应用程序上运行 rake middleware 会得到以下结果:

    use Rack::Lock
    use ActionController::Failsafe
    use ActionController::Reloader
    use ActiveRecord::ConnectionAdapters::ConnectionManagement
    use ActiveRecord::QueryCache
    use ActiveRecord::SessionStore, #<Proc:0x017fb394@(eval):8>
    use ActionController::RewindableInput
    use ActionController::ParamsParser
    use Rack::MethodOverride
    use Rack::Head
    run ActionController::Dispatcher.new
    

    机架堆栈上的第一项是Rack::Lock。这会对每个请求进行锁定,因此一次只处理一个请求。因此,标准 rails 应用程序是单线程的。但是,您可以在使您的应用程序多线程的请求中生成新线程,大多数人从未遇到过这种情况。

    如果您遇到问题……

    require 'thread'
    Thread.exclusive do
         # stuff here
    end
    

    ... 将确保块内的内容永远不会与 any 其他代码并行运行。在所有线程之间创建共享Mutext(在类变量或其他东西中,但是在开发模式下重新加载时可能会被擦除,所以要小心),如果你只想像Rack::Lock#call那样锁定它是首选以确保不会同时执行相同代码的两个实例。

    此外,为了记录,每个请求在每个请求周期中创建和取消引用一个控制器。没有两个请求应该看到同一个实例,尽管它们可能看到同一个类。

    设置config.threadsafe! 使我所说的几乎所有内容都无效。这会从堆栈中删除 Rack::Lock,这意味着您需要手动设置互斥锁以防止重复输入。除非你有充分的理由,否则不要这样做。

    即使没有Rack::Lock,您仍然会在每个请求中获得一个控制器实例。控制器的入口点确保 notice the call to new in process.

    【讨论】:

    • 您能否解决 cwa 的回答以及 Mongrel 常见问题解答中的这句话,“当 Rails 运行时,一次只有一个控制器在运行。”这是什么意思,在一次运行中......多个线程通过一个实例传递,如 Java servlet?
    • 顺便说一句,我最终在 Mutex 信号量上进行了同步,所以我的代码是安全的,毫无疑问,但我还是想了解这一点。感谢您的回答...
    • Rails 是单线程的。每个请求都有自己的控制器实例,一次只处理一个线程。
    • 正确。许多进程(杂种),每个进程一个线程(rails 应用程序)。
    • 不,这仅适用于单个进程。要在进程之间锁定,您将需要一些进程间流控制,在单台计算机上,您可以使用文件文件锁定、跨机器数据库锁定(但这完全是另一个问题)。
    【解决方案2】:

    我的理解是每个控制器处理的 HTTP 请求都会创建一个新的控制器实例。

    【讨论】:

    • 对,那和servlet完全一样。那么线程竞争可能是单线程资源的问题。
    • 看来是错的,约翰。请您验证并更正您的答案或删除它吗?
    • 你为什么要写这个?控制器实例变量仅在一个请求的生命周期内存在。
    • 对不起,约翰,我正试图理解这一点。查看其他答案。引用某些内容可能会有所帮助。
    • 我很可能是错的。我确定我在某处读到每个请求都会创建一个新的控制器实例,但通常我现在找不到参考!谷歌搜索并没有提供太多信息。
    【解决方案3】:

    Ruby 是单线程的。所以在任何时候,控制器一次只能处理一个请求。如果有多个请求,则将这些请求排队。为了避免这种情况,人们通常会运行一小组 Mongrel 以获得良好的并发性。它是这样工作的(直接来自 Mongrel WIKI FAQ):

    1. 一个请求命中了 mongrel。
    2. Mongrel 制作线程并解析 HTTP 请求头
    3. 如果正文很小,则将正文放入 StringIO
    4. 如果正文较大,则将正文流式传输到临时文件
    5. 当请求“熟”时,它会调用 RailsHandler。
    6. RailsHandler 查看文件是否可能是页面缓存,如果是,则发送缓存页面。
    7. 现在您终于可以处理 Rails 请求了。锁定!
    8. 仍处于锁定状态,Mongrel 调用 Rails Dispatcher 来处理请求,传入标头和 StringIO 或 Tempfile 作为正文。
    9. Rails 完成后,解锁! . Rails 已(希望)将其所有输出放入 StringIO。
    10. Mongrel 然后获取这个 StringIO 输出、任何输出标头,并将它们以超快的速度流回客户端。

      注意,如果页面被缓存,如果没有锁定。

    【讨论】:

    • Ruby 不是单线程的。 GIL 并不意味着它是单线程的。
    猜你喜欢
    • 2015-07-22
    • 2012-12-18
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多