【问题标题】:Ruby, windows, active_record, and Control-CRuby、windows、active_record 和 Control-C
【发布时间】:2010-10-19 17:51:00
【问题描述】:

active_record 对 windows 下的信号处理做了什么(我在 mac 上没有看到相同的版本)导致它的行为如此奇怪?例如:

require 'rubygems'
trap("INT"){puts "interrupted"}
puts __LINE__
sleep 5
require 'active_record'
trap("INT"){puts "interrupted again"}
puts __LINE__
sleep 5

当我运行上面的代码(ruby 1.8.6、gem 1.3.1、activerecord 2.2.2)时,我可以在第一次睡眠期间按我喜欢的次数多次点击 ^C,但在 require 之后的第一次中断activerecord 导致脚本终止。在上述情况下,陷阱仍然执行,只是无法让程序继续执行。通常。

删除第二个陷阱调用不会对行为产生任何影响。

真正令人烦恼的是,在某些情况下,陷阱根本无法执行。考虑到这样做的全部目的是让我的代码自行清理(删除它在数据库中的足迹,以便下一个人看到一个理智的状态),这是一个真正的问题。例如:

require 'rubygems'
require 'active_record'
trap("INT"){puts "interrupted"}
puts __LINE__
gets

看到 puts 后按 ^C 根本不会执行陷阱。

我只在需要 active_record 后才看到这个问题。有解决方法吗?我很想知道这是一个错误还是有某种解释。正如我所说,我在 mac 上对此没有任何问题 - 重复 ^Cs 会导致陷阱过程的多次执行。

谢谢...

【问题讨论】:

  • 嘿,这是 Windows 上的 Ruby。很高兴它不会让你的机器着火(我认为他们在 1.4 中修复了这个错误)。
  • 只是好奇,但为什么有必要捕获任何 SIGINT,而不是在代码的某些部分中挽救 ruby​​ 的中断异常?也许将您的解决方案缩小到只需要长时间运行的进程就足够了,而不是在代码中的任何地方捕获 any SIGINT
  • 如果您想进一步测试陷阱的调用,您可以调用Process.kill "INT", $$,如ntecs.de/old-hp/s-direktnet/ruby/uguide18.html。此外,trap 的返回值是它会执行的前一个 Proc,因此您可以检查活动记录是否正在改变它。在我的 Mac 上似乎没有。
  • 您能否使用Process.kill 添加上述代码的一个版本,该版本在不让用户按Ctrl-C 的情况下执行显示问题?
  • 这是另一篇可能有帮助的文章,虽然我不确定为什么 TERM 会在 Windows 上执行:kseebaldt.blogspot.com/2009/02/it-trap.html

标签: ruby windows activerecord exception-handling interrupt


【解决方案1】:

我在尝试复制此问题时看到了不同的模式:

puts "start"
trap("INT") { puts "interrupted" }
sleep 5
puts "end"

在 Ubuntu (Ruby 1.8.6) 上会产生

start
interrupted
interrupted
(etc)
interrupted
end

所以每次按下 Crtl-C 时都会“中断”打印,直到 5 秒结束。在 Windows(也是 Ruby 1.8.6)下,这会产生:

start
interrupted
end

即它打印一次“中断”然后退出。

看来,在处理 SIGINT 时,Ruby 退出了睡眠例程并继续执行下一条语句。我的猜测(挥手)是这在某种程度上是由于 Ruby 在 Windows 上使用绿色线程而不是本机线程。有高手请在此指点一下。

您可以通过在处理程序中重新启动 sleep 来模拟 Unix-y 行为:

puts "start"
trap("INT") do 
  puts "interrupted"
  sleep 5
end
sleep 5
puts "end"

不幸的是,每次 SIGINT 被捕获时都会重置计时器,所以它需要一些黑客攻击:

$interval = 5
def go_to_sleep(secs)
  $started = Time.now
  sleep secs
end
trap("INT") do 
  puts "interrupted"
  time_to_sleep = [0,$interval - (Time.now - $started)].max
  if time_to_sleep > 0
    sleep time_to_sleep
  end
end
puts "one"
go_to_sleep($interval)
puts "two"
go_to_sleep($interval)
puts "three"
go_to_sleep($interval)

【讨论】:

    【解决方案2】:

    考虑到这样做的全部目的是让我的代码自行清理(删除它在数据库中的足迹......

    您是否考虑过只使用数据库事务?这似乎是解决问题的一种更简单的方法。

    【讨论】:

    • 问题是脚本运行了 10-60 分钟并进行了多次读取/写入 - 它不能被事务化。
    猜你喜欢
    • 1970-01-01
    • 2019-07-28
    • 2016-12-23
    • 1970-01-01
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多