So a Thread will execute until it is joined, so we have to ask it nicely to go away somehow. As mentioned I would probably subclass it and create methods that allowed for a clean way of referencing the subprocess that it may have spawned and terminating it gracefully like so:
class InterruptibleThread(Thread): def __init__(self, target): super().__init__() self.target = target self.process = None self._stopevent = Event() self._periodicity = 1.0 def run(self): self.process = Popen(self.target) while not self._stopevent.is_set(): self._stopevent.wait(self._periodicity) def stop(self): self._stopevent.set() self.process.terminate() Thread.join(self)
I am on a Windows machine, so the following sequence opens a Command Prompt window and terminates when the spawned thread has its stop() method called:
>>> t = InterruptibleThread('cmd') >>> t.target 'cmd' >>> t.process # Returns None as it should >>> t.start() >>> t.process <subprocess.Popen object at 0x00000295ACC704E0> >>> t.process.poll() # Returns None as it should >>> t.stop() >>> t.process.poll() 1 >>>
Now in your program you'll likely have some logic that requires you to have a t.join() line, which is fine, here I just force it by using t.stop() which ultimate joins the thread so your programs call to t.join() will work by either overriding the join() method or somewhere else in your program calling the defined t.stop(). The t.process.poll() checks to see if the process was terminated (just shown here for demonstration purposes). So now you have a way of terminating a thread and its subprocesses.