Most Pythonic way to kill a thread after some period of time

Using an Event in this case is works just fine as the signalling mechanism, and is actually recommended in the threading module docs.

If you want your threads to stop gracefully, make them non-daemonic and use a suitable signalling mechanism such as an Event.

When verifying thread termination, timeouts almost always introduce room for error. Therefore, while using the .join() with a timeout for the initial decision to trigger the event is fine, final verification should be made using a .join() without a timeout.

# wait 30 seconds for the thread to finish its work
t.join(30)
if t.is_alive():
    print "thread is not done, setting event to kill thread."
    e.set()
    # The thread can still be running at this point. For example, if the 
    # thread's call to isSet() returns right before this call to set(), then
    # the thread will still perform the full 1 second sleep and the rest of 
    # the loop before finally stopping.
else:
    print "thread has already finished."

# Thread can still be alive at this point. Do another join without a timeout 
# to verify thread shutdown.
t.join()

This can be simplified to something like this:

# Wait for at most 30 seconds for the thread to complete.
t.join(30)

# Always signal the event. Whether the thread has already finished or not, 
# the result will be the same.
e.set()

# Now join without a timeout knowing that the thread is either already 
# finished or will finish "soon."
t.join()

I'm way late to this game, but I've been wrestling with a similar question and the following appears to both resolve the issue perfectly for me AND lets me do some basic thread state checking and cleanup when the daemonized sub-thread exits:

import threading
import time
import atexit

def do_work():

  i = 0
  @atexit.register
  def goodbye():
    print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
           (i, threading.currentThread().ident))

  while True:
    print i
    i += 1
    time.sleep(1)

t = threading.Thread(target=do_work)
t.daemon = True
t.start()

def after_timeout():
  print "KILL MAIN THREAD: %s" % threading.currentThread().ident
  raise SystemExit

threading.Timer(2, after_timeout).start()

Yields:

0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]