Terminating a Thread






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 'thread'

	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't 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

It's 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, there's 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.

Here's 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

Here's the CounterThread class from the Solution, implemented as a LoopingThread. I've 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



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows