2009-04-15 14 views
1

Was macht active_record mit den Signalprozessen unter Windows (ich sehe das nicht mit den gleichen Versionen auf dem Mac), dass es sich so seltsam verhält? Zum Beispiel:Ruby, windows, active_record und Control-C

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

Wenn ich den obigen Code ausführen (Rubin 1.8.6, gem 1.3.1, 2.2.2 Active,) schlage ich kann^C so oft, wie ich im ersten Schlaf gefallen, aber Der erste Interrupt nach der Anforderung von activerecord bewirkt, dass das Skript beendet wird. Im obigen Fall wird der Trap weiterhin ausgeführt, das Programm kann jedoch nicht fortgesetzt werden. Gewöhnlich.

Das Entfernen des zweiten Aufrufs zum Trap hat keine Auswirkung auf das Verhalten.

Der eigentliche Ärger ist, dass unter bestimmten Bedingungen die Falle überhaupt nicht ausgeführt wird. Wenn man bedenkt, dass der Sinn darin liegt, meinen Code nach sich selbst aufzuräumen (entferne seinen Footprint in der Datenbank, so dass der nächste Typ einen gesunden Zustand sieht), ist das ein echtes Problem. Zum Beispiel:

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

Drücken von^C nach dem Puts sehen wird die Falle überhaupt nicht ausführen.

Ich sehe dieses Problem nur nach der Anforderung active_record. Gibt es eine Problemumgehung? Ich wäre neugierig zu wissen, ob das ein Fehler ist oder ob es irgendeine Erklärung gibt. Wie gesagt, ich habe kein Problem damit auf dem Mac - wiederholte Cs führen zu mehreren Ausführungen des Trap-Prozesses.

dank ...

+0

Hey Es ist Ruby unter Windows. Sei einfach glücklich, dass es deine Maschine nicht in Brand setzt (ich denke, dass sie diesen Fehler in 1.4 behoben haben). – Pesto

+0

Nur neugierig, aber warum ist es notwendig, irgendein SIGINT zu fangen, anstatt Rubys Unterbrechungsausnahme innerhalb eines Teils Ihres Codes zu retten? Vielleicht würde es ausreichen, Ihre Lösung auf den langwierigen Prozess zu beschränken, anstatt * irgendeinen * SIGINT * irgendwo in Ihrem Code * einzufangen. –

+0

Wenn Sie die Aufrufe von Traps weiter testen möchten, können Sie 'Process.kill" INT ", $$' wie in http://www.ntecs.de/old-hp/sdirektnet/ruby/uguide18.html aufrufen. Auch der Rückgabewert von Trap ist der vorherige Proc, den er ausgeführt hätte, so dass Sie überprüfen können, ob der aktive Datensatz das ändert. Es scheint nicht so auf meinem Mac zu sein. –

Antwort

1

Bedenkt man, dass der ganze Sinn, dies zu tun ist mein Code nach sich aufzuräumen zu erhalten (seine Präsenz in der Datenbank entfernen ...

Sie haben Wird nur eine Datenbanktransaktion verwendet? Es scheint, als wäre es eine viel einfachere Möglichkeit, das Problem zu lösen.

+0

Das Problem ist, dass das Skript für 10-60 Minuten läuft und eine Reihe von liest/schreibt - es kann nicht transaktionalisiert werden. – Sniggerfardimungus

0

Ich sah ein anderes Muster, wenn Sie versuchen, dieses Problem zu duplizieren:

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

auf Ubuntu (Rubin 1.8.6) erzeugt dies

start 
interrupted 
interrupted 
(etc) 
interrupted 
end 

So "unterbrochen" druckt jedes Mal Strg-C gedrückt wird, bis die 5 Sekunden sind. Unter Windows (auch Ruby 1.8.6) erzeugt dies:

d. H. Es wird einmal "unterbrochen" und dann beendet.

So scheint es, dass während der Behandlung von SIGINT Ruby die Schlafroutine beendet und mit der nächsten Anweisung fortfährt. Meine Vermutung (Hand-winkend) ist, dass dies irgendwie darauf zurückzuführen ist, dass Ruby grüne Threads anstelle von nativen Threads unter Windows verwendet. Irgendwelche Experten läuten hier bitte ein.

Sie konnten die Unix-y Verhalten emulieren, indem sleep im Handler neu zu starten:

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

Leider dies setzt den Timer jedes Mal SIGINT gefangen ist, so dass es einige braucht Hacking:

$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)