How to create a resetable "process" or "thread" in Ruby?
I've got this script, that uploads some files, connects via ssh and does some stuff on the remote server, kinda like deployment script, and I want to make it run whenever I want to and to be able to reset i开发者_如何学Got in the middle of processing.
def deploy
# some stuff happens here
end
def do_deploy
$deploy.kill! if $deploy
$deploy = Thread.new { deploy }
$deploy.join # when I don't have the join here, it stop executing the deploy
# like after first when thread switches to something else, and
# then never finishes
end
reading = Thread.new do
while line = gets.chomp!
case line
when "q" then
puts "exiting"
Thread.exit
exit
when "r" then
do_deploy
end
end
end
reading.join
Even though the $deploy.join
makes the whole deploy thread execute, it prevents reading any input, so I can't reset it in the middle of execution. And I can't join it at the end of the script.
What I essentially need to do, is to run a task while listening for an input and be able to kill the process and restart it at any given time. Or even better, send it a message that would get processed immediately, like shut down test execution.
I know that killing threads isn't really a neat thing to do, but in this case, I don't think that it's a big issue.
I'd also like to point out, that I'm working on Windows, so I don't have fork()
available.
Is there any other way how to solve this without threads?
edit: Just found out, that calling gets
blocks all other Threads from execution until any input is given. In this example
t1 = Thread.new { 10.times { |i| puts i } }
t2 = Thread.new { puts gets }
t1.join
t2.join
the t1 doesn't get executed untill I give some input to the gets
. It just keeps sitting there forever. How should I read from input without blocking all threads?
edit2: just found out, that this is a Windows related issue
edit3: the problem goes away in JRuby or Ruby 1.9
When you use Thread.join
, the calling thread will block until the specified thread terminates. This is the reason you are unable to process input at this point (do_deploy
is no longer being executed).
The deploy
thread should be executing as soon as you call Thread.new
. If I am reading your code correctly, you want to move your call to .join
from inside do_deploy
to inside your reading
block. Try using the following for your reading
block (I don't have Ruby in front of me so this may not be perfect syntax):
reading = Thread.new do
while line = gets.chomp!
case line
when "q" then
puts "exiting"
$deploy.kill if $deploy
break
when "r" then
do_deploy
end
end
$deploy.join
end
This should launch (or kill and re-launch) the deploy
process when "r" is input and then return to the input processing loop. Input a "q" and the deploy
process will be sent the "terminate" signal and the input processing loop will wait for it to terminate before terminating itself.
With Ruby threads, you may run into trouble where it seems like the input processing thread isn't running when the deploy
thread is working (busy threads can starve out low-priority threads). If this happens to you, I would recommend adding some explicit calls to Thread.pass
in your deploy
routine. This will force the thread scheduler to let another thread run. You may also want to experiment using thread priorities and placing your input processing thread at a higher priority than your worker thread.
Use JRuby. It has real threads and doesn't fall prey to the GIL (Global Interpreter Lock) that the C Ruby interpreter has.
http://www.jruby.org/
精彩评论