5

Suppose I would like to run a function, called run_forever(), in a thread, but still have it 'stoppable' by pressing Ctrl+C. I've seen ways of doing this using a StoppableThread subclass of threading.Thread, but these seem to involve 'copying' the target function into that subclass. I would like to instead keep the function 'where it is'.

Consider the following example:

import time
import threading

def run_forever():  # An externally defined function which runs indefinitely
    while True:
        print("Hello, world!")
        time.sleep(1)

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 = threading.Event()

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

    def stopped(self):
        return self._stop.isSet()

    def run(self):
        while not self.stopped():
            run_forever()             # This doesn't work
            # print("Hello, world!")      # This does
            self._stop.wait(1)

thread = StoppableThread()
thread.start()
time.sleep(5)
thread.stop()

The target function run_forever is itself a while-loop which never exits. However, to get the desired behavior the wait() command has to be inside that while-loop, as I understand it.

Is there any way of achieving the desired behavior without modifying the run_forever() function?

3
  • 2
    If you simply want your program to be " 'stoppable' by pressing Cntrl+C" - which I interpret to mean "will not hang when I try to kill the program" - you can set the daemon attribute in the Thread object to True as such: thread = StoppableThread(daemon=True). Commented Nov 2, 2016 at 15:38
  • Billy, I already have such an implementation with daemonized threads, but these are stopped abruptly. I would like something that stops gracefully. Commented Nov 2, 2016 at 15:41
  • 1
    If you absolutely cannot change the run_forever function, I think your best chance would be to access the platform API directly, similarly to the second example in the answer here: stackoverflow.com/a/325528/7007605 Commented Nov 2, 2016 at 16:19

1 Answer 1

3

I doubt it's possible.
BTW, have you tried the second solution with ThreadWithExc from the post you linked earlier?
It works if the loop is busy pure Python(eg no sleep), otherwise I'd switch to multiprocessing and kill subprocess. Here is the code that hopefully exits gracefully(*nix only):

from multiprocessing import Process
from signal import signal, SIGTERM
import time

def on_sigterm(*va):
    raise SystemExit

def fun():
    signal(SIGTERM, on_sigterm)
    try:
        for i in xrange(5):
            print 'tick', i
            time.sleep(1)
    finally:
        print 'graceful cleanup'

if __name__=='__main__':
    proc = Process(target=fun)
    proc.start()
    time.sleep(2.5)
    proc.terminate()
    proc.join()
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.