【问题标题】:How to create thread at Rails startup?如何在 Rails 启动时创建线程?
【发布时间】:2013-01-03 00:22:29
【问题描述】:

我正在尝试在 Rails 启动时创建一个线程,该线程将在应用程序的整个生命周期中运行。奇怪的是,我已经使用我正在运行的另一个线程进行了此操作。我复制了该(工作)代码并将其用作新线程的新代码的样板。但线程不会启动。

  • 代码在 config/initializers 中(这是正确的位置吗?)。
  • 文件以“z_...”开头命名,以确保它最后运行。
  • Rails 3.2.6

下面是代码的一般结构:

class Blah
  def self.the_thread
    The_Model.transaction do
    # do some database stuff
    # ...
  end

  TheThread = Thread.new do
    while true do
      the_thread
      sleep 5.seconds
    end
  end
end

打开 Rails 控制台并检查 Blah::TheThread 会发现一个似乎从未运行过的死线程。类的声明和方法中似乎没有任何错误,因为我可以在打开 rails 控制台时运行该方法并且它工作得很好。此外,如果我在 Rails 控制台中手动输入生成线程的确切代码(TheThread = Thread new do ...),它工作得很好(每 5 秒唤醒一次,做它的事情,再次休眠)。

再一次,奇怪的是,这个在 Rails 中生成一个简单线程的确切样板在这个完全相同的应用程序中为我工作过。

如果有人对我认为奇怪的问题有一些可能的见解,我会全神贯注。

谢谢。

编辑:新信息 - 我刚刚注释掉了事务调用(和匹配结束),它工作正常。我不知道交易可能会干扰什么。我删除了我的另一个自定义初始化脚本,目录中唯一的其他脚本要么是默认脚本,要么是属于 devise 的脚本。我仔细阅读了它们,没有看到任何交易正在进行。

更多新信息。我在线程代码的末尾添加了“TheThread.abort_on_exception = true”。当我启动 rails 时,我现在得到一个异常“ArgumentError:在关闭的数据库上调用准备:回滚事务(ActiveRecord::StatementInvalid)”。错误发生在 sqlite3 gem 中。我有 sqlite3 gem 版本 1.3.6 和 sqlite-ruby gem 版本 1.3.3。我用谷歌搜索了这个错误,发现有几个帖子抱怨同样的错误,但没有看到任何解决方案。

我不再认为这是一个线程问题,而是一个数据库问题,但来不及改变这篇文章的标题。

这只是在:我在线程代码的顶部(就在它进入 while 循环之前)放置了一个“sleep 15.seconds”,这样就解决了问题。但我仍然想知道是什么导致了问题,以及什么是“非黑客”解决方案。由于某种原因,此代码运行时数据库尚未准备好。我的初始化代码是不是放错地方了?

【问题讨论】:

  • 这可能是您的代码的问题。如果您复制和粘贴内容,很有可能您使用了相同的类名,并且以某种方式确实覆盖了其他线程代码或类似的内容。
  • 我已经检查了每一寸代码(没有太多 - 大约一页)。除了基本结构之外,没有任何共同点(变量名、类名等)。我什至删除了其他代码作为测试,但这无济于事。
  • 我很好奇你为什么要这样做?假设是因为您希望在后台进程中运行某些东西。如果是这样,你为什么不看一下delayed_job、resque 或sidekiq?
  • 我不想为一个非常简单的任务陷入复杂或繁重的任务,该任务可以在半页中编码,每天只需要运行一次('sleep 5 seconds ' 上面用于测试)。
  • 然后使用带有纯文件的 cron 作业。线程很繁重,在这里自找麻烦

标签: ruby-on-rails multithreading configuration initialization


【解决方案1】:

我真的建议寻找另一种方法来实现这一点。除非您在投入生产时确实需要 1 个额外线程每个实例(想象一下设置 3-4 个瘦/杂种/等服务器 - 每个都有额外的线程),否则有更好的方法去做它。

选项 1 - 最简单的 - 只是创建一个将执行您的任务的控制器操作,然后使用 wget 和 cron 定期触发该操作。

20 * * * *  /usr/bin/wget --quiet -O - 'http://www.mydomain.com/my_controller/my_action'

这里的问题是您必须处理身份验证/访问控制 - 除非您真的不在乎某个随机用户是否可以触发您的工作(坦率地说,有时非常简单的工作就是这种情况)。

选项 2 - 使用 Clockwork/Sidekiq 或 BackgroundRb 或其他在后台运行任务的方式。我已经在生产应用程序中完成了这项工作,并且效果非常好。我选择了 Clockwork 和 Sidekiq,我的计划任务如下所示:

app/workers/daily_worker.rb:

class DailyWorker
  include Sidekiq::Worker

  def perform
    # do stuff here
  end
end

clockwork.rb:

require 'clockwork'
require 'sidekiq'

Dir["app/workers/*"].each {|f| load f }

module Clockwork
  every 1.day, 'daily.jobs', :at => "00:30" do
    DailyWorker.perform_async
  end
end

这对我来说似乎是最好的解决方案 - 我只花了大约 15 分钟的时间就将它全部启动并运行起来。

【讨论】:

  • 当这就是你真正想要的时候,没有答案怎么做
猜你喜欢
  • 2013-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 2010-12-31
相关资源
最近更新 更多