2009-04-15 3 views
1

Что такое active_record, выполняющее сигнальные процессы под окнами (я не вижу этого с теми же версиями на Mac), что заставляет его вести себя так странно? Например:Ruby, windows, active_record и Control-C

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

Когда я запускаю приведенный выше код (рубин 1.8.6, камень 1.3.1, 2.2.2 ActiveRecord), я могу нажать^C, как много раз, как я люблю во время первого сна, но первое прерывание после требования activerecord приводит к завершению работы скрипта. В приведенном выше случае ловушка все еще выполняется, она не позволяет программе продолжать работу. Как правило.

Удаление второго вызова ловушки не влияет на поведение.

Настоящая досада заключается в том, что в некоторых условиях ловушка не выполняется вообще. Учитывая, что все это делается для того, чтобы очистить мой код после себя (удалить его след в базе данных, чтобы следующий парень увидел нормальное состояние), это настоящая проблема. Например:

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

Нажатие кнопки^C после того, как пушки не будут запускать ловушку.

Я вижу эту проблему только после того, как требуется active_record. Есть ли обходной путь? Мне было бы интересно узнать, является ли это ошибкой или есть какие-то объяснения. Как я уже сказал, у меня нет проблем с этим на mac - повторение^Cs приводит к нескольким запускам ловушки proc.

благодаря ...

+0

Эй , это Ruby на Windows. Просто будьте счастливы, если он не включит вашу машину (я думаю, что они исправили эту ошибку в 1.4). – Pesto

+0

Просто любопытно, но зачем нужно перехватывать любой SIGINT, а не спасать исключение исключения Ruby в некоторой части вашего кода? Возможно, сужение вашего решения до продолжительного процесса было бы достаточно, а не ловли * любой * SIGINT * в любом месте вашего кода *. –

+0

Если вы хотите дополнительно протестировать вызовы ловушки, вы можете вызвать 'Process.kill 'INT', $$' как в http://www.ntecs.de/old-hp/s-direktnet/ruby/uguide18.html. Кроме того, возвращаемое значение ловушки - это предыдущий Proc, который он выполнил бы, поэтому вы можете проверить, изменила ли активная запись это. Кажется, это не так на моем mac. –

ответ

1

Учитывая, что весь смысл делать это, чтобы получить мой код, чтобы убирать за собой (удалить его след в базе данных ...

у вас рассмотренный как раз используя транзакцию базы данных? Кажется, что это был бы гораздо более простой способ решить эту проблему.

+0

Проблема в том, что скрипт работает в течение 10-60 минут и выполняет ряд операций чтения/записи - он не может быть транзакционирован. – Sniggerfardimungus

0

При попытке дублирования этой проблемы был обнаружен другой шаблон:

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. Любые эксперты, пожалуйста, звоните здесь.

Вы могли эмулировать Unix-у поведения при перезапуске sleep в обработчике:

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)