0

This is really weird to me. Have been frustrated for hours. Once a RUN request received, RUNNER will start out a background thread running forever. At anytime, there will be only one running thread (i use mutex). The problem is, for status request, sometimes it returns true, sometimes it returns false.

(done method never been called, since the backend thread will run forever, so there is no way is_running? could be false)

#config/initializers/some_init.rb
RUNNER = Runner.instance

#app/controllers/some_controller.rb
class SomeController < ApplicationController
  def run
    RUNNER.run {
      loop do
        Rails.logger.debug "backend thread running #{Time.zone.now}"
        sleep(5)
      end
    }
  end

  def status
    Rails.logger.debug RUNNER.inspect
    RUNNER.is_running?
  end
end

#lib/runner.rb
require 'singleton'
class Runner
  include Singleton
  def initialize
    @running = false
    @mutex = Mutex.new
  end

  def run(&blk)
    @mutex.synchronize do
      return if @running #only one job run is allowed at anytime

      @running = true
      @thr = Thread.new(self) do |r|
        blk.call
        r.done
      end 
    end
  end

  def is_running?
    @mutex.synchronize{ @running }
  end

  private
  def done
    @mutex.synchronize {@running = false}
  end
end
4

1 回答 1

4

简短的回答:你不能这样做。

长答案是 Rails 由一个或多个处理传入请求的进程组成。在这些进程之一中随机启动一个线程不会使该is_running?标志成为除触发true该方法的进程之外的任何进程。run

大多数 Rails 托管环境会频繁地创建和销毁进程,但不能保证您的进程会比它当前正在处理的请求存活得更长。这意味着您的线程很可能会被意外终止。

Rails 严格来说是一个请求-响应系统。如果你需要一个后台进程,你必须将它创建为一个独立的脚本,而不是通过 Rack 参与的东西。

可以创建在 Rails 环境中运行的长时间运行的脚本,例如,以 开头rails runner,但这些脚本必须独立于您的应用程序启动。通常,您使用类似systemctl或的流程主管god来启动它并保持运行。

根据您的用例,像延迟作业这样的作业处理系统可能更适合您。这对于执行不适合面向 Web 的 Rails 进程模型的间歇性“后台任务”非常有用。

于 2013-06-28T03:14:24.920 回答