Most Pythonic Way to Kill a Thread After Some Period of Time

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()

Stopping a thread after a certain amount of time

This will work if you are not blocking.

If you are planing on doing sleeps, its absolutely imperative that you use the event to do the sleep. If you leverage the event to sleep, if someone tells you to stop while "sleeping" it will wake up. If you use time.sleep() your thread will only stop after it wakes up.

import threading
import time

duration = 2

def main():
t1_stop = threading.Event()
t1 = threading.Thread(target=thread1, args=(1, t1_stop))

t2_stop = threading.Event()
t2 = threading.Thread(target=thread2, args=(2, t2_stop))

time.sleep(duration)
# stops thread t2
t2_stop.set()

def thread1(arg1, stop_event):
while not stop_event.is_set():
stop_event.wait(timeout=5)

def thread2(arg1, stop_event):
while not stop_event.is_set():
stop_event.wait(timeout=5)

How to kill a thread after N seconds?

As I said in a comment you can't really "kill" a thread (externally). However they can "commit suicide" by returning or raising a exception.

Below is example of doing the latter when the thread's execution time has exceeded a given amount of time. Note that this is not the same as doing a join(timeout) call, which only blocks until the thread ends or the specified amount of time has elapsed. That's why the printing of value and its appending to the list happens regardless of whether the thread finishes before the call to join() times-out or not.

I got the basic idea of using sys.settrace() from the tutorial titled Different ways to kill a Thread — although my implementation is slightly different. Also note that this approach likely introduces a significant amount of overhead.

import sys
import threading
import time


class TimelimitedThread(threading.Thread):
def __init__(self, *args, time_limit, **kwargs):
self.time_limit = time_limit
self._run_backup = self.run # Save superclass run() method.
self.run = self._run # Change it to custom version.
super().__init__(*args, **kwargs)

def _run(self):
self.start_time = time.time()
sys.settrace(self.globaltrace)
self._run_backup() # Call superclass run().
self.run = self._run_backup # Restore original.

def globaltrace(self, frame, event, arg):
return self.localtrace if event == 'call' else None

def localtrace(self, frame, event, arg):
if(event == 'line' and
time.time()-self.start_time > self.time_limit): # Over time?
raise SystemExit() # Terminate thread.
return self.localtrace


THREAD_TIME_LIMIT = 2.1 # Secs
threads = []
my_list = []

def foo(value):
global my_list
time.sleep(value)
print("Value: {}".format(value))
my_list.append(value)

for i in range(5):
th = TimelimitedThread(target=foo, args=(i,), time_limit=THREAD_TIME_LIMIT)
threads.append(th)

for th in threads:
th.start()

for th in threads:
th.join()

print('\nResults:')
print('my_list:', my_list)

Output:

Value: 0
Value: 1
Value: 2

Results:
my_list: [0, 1, 2]

Is there any way to kill a Thread?

It is generally a bad pattern to kill a thread abruptly, in Python, and in any language. Think of the following cases:

  • the thread is holding a critical resource that must be closed properly
  • the thread has created several other threads that must be killed as well.

The nice way of handling this, if you can afford it (if you are managing your own threads), is to have an exit_request flag that each thread checks on a regular interval to see if it is time for it to exit.

For example:

import threading

class StoppableThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""

def __init__(self, *args, **kwargs):
super(StoppableThread, self).__init__(*args, **kwargs)
self._stop_event = threading.Event()

def stop(self):
self._stop_event.set()

def stopped(self):
return self._stop_event.is_set()

In this code, you should call stop() on the thread when you want it to exit, and wait for the thread to exit properly using join(). The thread should check the stop flag at regular intervals.

There are cases, however, when you really need to kill a thread. An example is when you are wrapping an external library that is busy for long calls, and you want to interrupt it.

The following code allows (with some restrictions) to raise an Exception in a Python thread:

def _async_raise(tid, exctype):
'''Raises an exception in the threads with id tid'''
if not inspect.isclass(exctype):
raise TypeError("Only types can be raised (not instances)")
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
ctypes.py_object(exctype))
if res == 0:
raise ValueError("invalid thread id")
elif res != 1:
# "if it returns a number greater than one, you're in trouble,
# and you should call it again with exc=NULL to revert the effect"
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), None)
raise SystemError("PyThreadState_SetAsyncExc failed")

class ThreadWithExc(threading.Thread):
'''A thread class that supports raising an exception in the thread from
another thread.
'''
def _get_my_tid(self):
"""determines this (self's) thread id

CAREFUL: this function is executed in the context of the caller
thread, to get the identity of the thread represented by this
instance.
"""
if not self.isAlive():
raise threading.ThreadError("the thread is not active")

# do we have it cached?
if hasattr(self, "_thread_id"):
return self._thread_id

# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
self._thread_id = tid
return tid

# TODO: in python 2.6, there's a simpler way to do: self.ident

raise AssertionError("could not determine the thread's id")

def raiseExc(self, exctype):
"""Raises the given exception type in the context of this thread.

If the thread is busy in a system call (time.sleep(),
socket.accept(), ...), the exception is simply ignored.

If you are sure that your exception should terminate the thread,
one way to ensure that it works is:

t = ThreadWithExc( ... )
...
t.raiseExc( SomeException )
while t.isAlive():
time.sleep( 0.1 )
t.raiseExc( SomeException )

If the exception is to be caught by the thread, you need a way to
check that your thread has caught it.

CAREFUL: this function is executed in the context of the
caller thread, to raise an exception in the context of the
thread represented by this instance.
"""
_async_raise( self._get_my_tid(), exctype )

(Based on Killable Threads by Tomer Filiba. The quote about the return value of PyThreadState_SetAsyncExc appears to be from an old version of Python.)

As noted in the documentation, this is not a magic bullet because if the thread is busy outside the Python interpreter, it will not catch the interruption.

A good usage pattern of this code is to have the thread catch a specific exception and perform the cleanup. That way, you can interrupt a task and still have proper cleanup.

How to stop running my threads after a period of time?

I think this is what you are trying to achieve:

import threading
from queue import Queue
import os
import time

timeout = 120 # [seconds]

timeout_start = time.time()

def OpenWSN ():
print( "OpenWSN:")
os.system("echo -OpenWSN-")

def Wireshark():
print( "Wireshark:")
os.system("echo -Wireshark-")

def wrapper1(func, queue):
queue.put(func())

def wrapper2(func, queue):
queue.put(func())

q = Queue()
threading.Thread(target=wrapper1, args=(OpenWSN, q)).start()
threading.Thread(target=wrapper2, args=(Wireshark, q)).start()

cv = threading.Condition()
cv.acquire()
cv.wait( timeout )

print ("***************** End Simulation *************************")
print (" Simulation Time: {0}s".format( time.time() - timeout_start) )

os.system("echo -exit-")

This produces the following output:

C:\temp\StackExchange\StopRunningThread>python -B stop-running-thread.py
OpenWSN:
Wireshark:
-OpenWSN-
-Wireshark-
***************** End Simulation *************************
Simulation Time: 120.04460144042969s
-exit-

What is happening there - you are starting two threads, each starts separate process in the system. After the said threads were started, you return to your main thread, allocate a "lock" and wait until this lock is signaled, or time out takes place.
In this particular case nobody signals the lock, so the only chance to finish the application is to wait until the time out happens.
I would extend your application that it signals the lock in each thread function, so we can terminate the main thread only if both of thread functions terminate.
But that was not the part of your question, so I assume you can leave without signalling.

How can I kill a thread in python

If your thread is busy executing Python code, you have a bigger problem than the inability to kill it. The GIL will prevent any other thread from even running whatever instructions you would use to do the killing. (After a bit of research, I've learned that the interpreter periodically releases the GIL, so the preceding statement is bogus. The remaining comment stands, however.)

Your thread must be written in a cooperative manner. That is, it must periodically check in with a signalling object such as a semaphore, which the main thread can use to instruct the worker thread to voluntarily exit.

while not sema.acquire(False):
# Do a small portion of work…

or:

for item in work:
# Keep working…
# Somewhere deep in the bowels…
if sema.acquire(False):
thread.exit()


Related Topics



Leave a reply



Submit