Stopping a thread, ensuring that certain final code is run Stopping a thread, ensuring that certain final code is run multithreading multithreading

Stopping a thread, ensuring that certain final code is run


Instead of killing your thread, why don't you flip a variable that causes it to stop at a pre-defined point? If you let it cycle through and exit at the end of the loop you won't have nearly as much trouble.

For instance:

def show_wait_spinner  running = true  spinner = Thread.new do    while (running) do      print "*"      sleep 0.1      print "\b"      end  end  yield  running = false  spinner.joinendprint "A"show_wait_spinner{ sleep rand }puts "B"

When you call Thread#kill you have no idea where the thread is, and the thread isn't given an opportunity to clean up what it's doing. You can always kill the thread if your polite "stop running" request isn't respected.


I prefer your synchronized stop condition approach but you have a couple bugs:

  1. after you set the stop variable, you almost immediately end theprogram, so the thing that stops the thread is program exit, not theconditional test in the loop; use Thread#join to wait for the threadto exit normally and you'll get the consistent output you want.
  2. break in the synchronize block breaks out of the block, not theloop.

    def show_wait_spinner  stop = false  stopm = Mutex.new  spinner = Thread.new{    loop{      print "*"      sleep 0.1      print "\b"      break if stopm.synchronize{ stop }    }  }  yield  stopm.synchronize{ stop = true }  spinner.join  STDOUT.flushendprint "A"show_wait_spinner{ sleep rand }puts "B"

I would avoid any solution involving Thread#raise and Thread#kill as their behavior can never be predictable and correct, see Charles Nutter's rant about the brokenness of these methods.

The Mutex#synchronize is only necessary for this simple boolean flipping if you really care a lot about the precise timing around the race condition when the parent thread sets the var, which in this example isn't likely, so you could avoid the overhead and just set and read stop normally.


In your mutex example, you need to wait for the Thread to finish before exiting the method. Currently you set stop to true, then exit the method, print B and end your script before the spinner thread is able to wake up and print the last backspace to delete the * character.

Also, the break in stopm.synchronize{ break if stop } only exits the inner block, not the loop, so you need to use catch/throw or something.

However, you don't need the mutex. This works for me in 1.9.3:

def show_wait_spinner  exit = false  spinner = Thread.new{    loop{      print "*"      sleep 0.1      print "\b"       break if exit    }      }  yield  exit = true  spinner.joinend

Adding $stdout.sync = true at the top makes it work in 1.8.7.