Terminating a Thread

Problem

You want to kill a thread before the end of the program.

Solution

A thread terminates if it reaches the end of its code block. The best way to terminate a thread early is to convince it to reach the end of its code block. This way, the thread can run cleanup code before dying.

This thread runs a loop while the instance variable continue is true. Set this variable to false, and the thread will die a natural death:

require hread class CounterThread < Thread def initialize @count = 0 @continue = true super do @count += 1 while @continue puts "I counted up to #{@count} before I was cruelly stopped." end end def stop @continue = false end end counter = CounterThread.new sleep 2 counter.stop # I counted up to 3413544 before I was cruelly stopped.

If you need to stop a thread that doesn offer a stop-like function, or you need to stop an out-of-control thread immediately, you can always call Thread#terminate. This method stops a thread in its tracks:

t = Thread.new { loop { puts I am the unstoppable thread! } } # I am the unstoppable thread! # I am the unstoppable thread! # I am the unstoppable thread! # I am the unstoppable thread! t.terminate

Discussion

Its better to convince someone they should do something than to force them to do it. The same is true of threads. Calling Thread.terminate is a bit like throwing an exception: it interrupts the normal flow of execution in an unpredictable place. Worse, theres no equivalent of a begin/ensure construct for thread termination, so calling THRead.terminate may corrupt your data or leave your program in an inconsistent state. If you plan to stop a thread before the program is over, you should build that capability into the thread object itself.

A common type of thread implements a loop: threads that process requests from a queue, or that periodically poll for new data. In these, the end of an iteration forms a natural stopping point. These threads can benefit from some simple VCR-style controls: pause, unpause, and stop.

Heres a Thread subclass which implements a loop that can be paused or stopped in a predictable way. A code block passed into the Thread constructor would implement the entire loop, but the code block passed into the LoopingThread constructor should implement only one iteration of the loop. Setup and cleanup code should be handled in the methods before_loop and after_loop.

class LoopingThread < Thread def initialize @stopped = false @paused = false super do before_loop until @stopped yield Thread.stop if @paused end after_loop end end def before_loop; end def after_loop; end def stop @stopped = true end def paused=(paused) @paused = paused run if !paused end end

Heres the CounterThread class from the Solution, implemented as a LoopingThread. Ive added a reader method for count so we can peek at its value when the thread is paused:

class PausableCounter < LoopingThread attr_reader :count def before_loop @count = 0 end def initialize super { @count += 1 } end def after_loop puts "I counted up to #{@count} before I was cruelly stopped." end end counter = PausableCounter.new sleep 2 counter.paused = true counter.count # => 819438 sleep 2 counter.count # => 819438 counter.paused = false sleep 2 counter.stop # I counted up to 1644324 before I was cruelly stopped. counter.count # => 1644324

Категории