Interrput a thread

Fuzzyman fuzzyman at gmail.com
Tue Jan 4 10:12:48 EST 2011


On Dec 29 2010, 11:31 pm, gervaz <ger... at gmail.com> wrote:
> Hi all, I need to stop a threaded (using CTR+C or kill) application if
> it runs too much or if I decide to resume the work later.
> I come up with the following test implementation but I wanted some
> suggestion from you on how I can implement what I need in a better or
> more pythonic way. Here the code:

This is a case that .NET (C#) handles better than Python or Java.

It is unsafe to terminate an os level thread at an arbitrary point
because it may be executing code in a critical section. Both Java
and .NET used to provide ways to terminate threads "safely" by raising
an asynchronous exception in the thread. Releasing locks (etc) that
the thread holds could then be done in a finally section protecting
the code. Python doesn't allow you to abort threads.

Unfortunately the thread abort exception could also be raised in the
finally section - prematurely aborting the lock / resource cleanup.

Java handled this by deprecating thread aborting. (Python has never
had it I believe.)

.NET handled it by changing the semantics of thread aborting - the
thread abort exception will never be raised in a finally block. This
makes thread aborting safe, although technically you can subvert it by
putting all your code in a finally block (you can also catch and
cancel the thread abort exception).

The standard advice is to use a flag and do manual checking to abort
threads. This only works for fine grained operations and *doesn't*
work for very coarse grained operations or where there aren't
convenient places to check the flag. It's another place where people
sometimes have a genuine need/use case yet people will insist on
telling them they don't *really* want it...

Anyway, although there are ways based on ctypes to abort Python
threads it's not really safe. If you google you should find them,
hopefully with intelligible caveat emptor warnings...

All the best,

Michael Foord


>
> import os
> import signal
> import time
> from threading import Thread, current_thread
> from queue import LifoQueue, Empty
>
> COMMAND = {"STOP": 0, "NORMAL": 1}
> THREAD_NUM = 5
>
> lq = LifoQueue()
>
> print("{0}\n".format(os.getpid()))
>
> class InterceptInterrupt(Exception):
>     pass
>
> class Handler:
>     def __init__(self, queue):
>         self._queue = queue
>     def __del__(self):
>         print("Bye bye!")
>     def getHandler(self, signum, frame):
>         print("Interrupt raised!")
>         for _ in range(THREAD_NUM):
>             self._queue.put((COMMAND["STOP"], None))
>         raise InterceptInterrupt
>
> h = Handler(lq)
>
> signal.signal(signal.SIGINT, h.getHandler)
>
> for i in range(25):
>     lq.put((COMMAND["NORMAL"], i))
>
> def do_work(queue):
>     while True:
>         time.sleep(5)
>         try:
>             cmd, value = queue.get(block=False)
>             if cmd == COMMAND["STOP"]:
>                 print("{0}: STOP command
> received!".format(current_thread().name))
>                 break
>             elif cmd == COMMAND["NORMAL"]:
>                 print(value)
>         except Empty:
>             break
>
> threads = [Thread(target=do_work, args=(lq,)) for _ in
> range(THREAD_NUM)]
>
> for t in threads:
>     t.start()
>
> while not lq.empty():
>     try:
>         time.sleep(1)
>     except (IOError, InterceptInterrupt):
>         break
>
> for t in threads:
>     t.join()
>
> if lq.empty():
>     print("The queue is empty.")
> else:
>     print("The queue is NOT empty. Some additional work has to be
> done.")
>
> Thank you,
> Mattia




More information about the Python-list mailing list