2

I've already asked similar question many times, but still can't find appropriate solution.

I have and external function, that could run in some cases very long. I want to interrupt it after, say, 30 seconds.

How do I do that?

  1. Threads are good, but I can't stope them (i don't have access to external function's code)
  2. Multiprocessing is also not an option, because I run site under mod_wsgi.

Are there any reliable ways to stop the thread? Maybe using os.system or os.kill. If yes, then how?

External function only performs some caclulations, no network connections or file openings. So, I just need to stop process and clean up.

My code (actually was taken from some source):

def time_limit(timeout):
    def internal(function):
        def internal2(*args, **kw):
            class MyThread(threading.Thread):
                def __init__(self):
                    threading.Thread.__init__(self)
                    self.result = None
                    self.error = None

                def run(self):
                    try:
                        self.result = function(*args, **kw) # this is external
                    except Exception as e:
                        self.error = e

                def _stop(self):
                    if self.isAlive():
                        threading.Thread._Thread__stop(self)

            c = MyThread()
            c.start()
            c.join(timeout)
            counter = 0
            if c.isAlive():
                c._stop()
                raise TimeoutException
            if c.error:
                raise c.error
            return c.result
        return internal2
    return internal
Paul R
  • 2,631
  • 3
  • 38
  • 72

4 Answers4

1

You can't kill thread, but you can kill a process. If you can use processes instead threads, you can use multithreading module. Process can be terminated from parent application.

If you describe your task in more detail, maybe I can help more.

Jimilian
  • 3,859
  • 30
  • 33
0

No, there is no way to externally stop a thread. A thread is under complete control of the process that spawned it, killing it externally would mean you'd have to mess with process memory, which would usually mean that you render the process dysfunctional; what you'd usually do is handling messages in some form that allows threads to terminate on some external impulse themselves.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
  • No way. A thread can't be killed. It's an internal thing of a process, with which there is no clean, well-defined way to interact. Your thread must be willing terminate itself, basically. – Marcus Müller Feb 20 '15 at 16:22
  • What about other answers to this question? What do you think? – Paul R Feb 20 '15 at 16:24
  • @AndrewFount: castaway2000's answer doesn't address threads at all, but processes. Noctis' answer is basically adding a point that always allows you to call the thread's stop method within the thread's context, which is nothing less than adding a very slow way of letting threads stop themselves. The answer that you've accepted is what I said, but without the information *why* you can't kill threads, so I'm a bit sad you chose that one. – Marcus Müller Feb 22 '15 at 11:01
  • @AndrewFount: you're not really understanding, sorry. – Marcus Müller Feb 22 '15 at 15:41
0

It may not be the best way to terminate a thread, but this answer provides a way to kill a thread. Beware that you may also need to implement a way for threads to be unkillable in critical sections of their code.


For your convenience, the code and usage example are included below:

import threading
import sys

class StopThread(StopIteration): pass

threading.SystemExit = SystemExit, StopThread

class Thread2(threading.Thread):

    def stop(self):
        self.__stop = True

    def _bootstrap(self):
        if threading._trace_hook is not None:
            raise ValueError('Cannot run thread with tracing!')
        self.__stop = False
        sys.settrace(self.__trace)
        super()._bootstrap()

    def __trace(self, frame, event, arg):
        if self.__stop:
            raise StopThread()
        return self.__trace


class Thread3(threading.Thread):

    def _bootstrap(self, stop_thread=False):
        def stop():
            nonlocal stop_thread
            stop_thread = True
        self.stop = stop

        def tracer(*_):
            if stop_thread:
                raise StopThread()
            return tracer
        sys.settrace(tracer)
        super()._bootstrap()

################################################################################

import time

def main():
    test = Thread2(target=printer)
    test.start()
    time.sleep(1)
    test.stop()
    test.join()

def printer():
    while True:
        print(time.time() % 1)
        time.sleep(0.1)

if __name__ == '__main__':
    main()

The Thread3 class appears to run code approximately 33% faster than the Thread2 class.

Community
  • 1
  • 1
Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
  • I saw that answer. Code under it is unacceptably slow. – Paul R Feb 20 '15 at 16:18
  • 1
    @AndrewFount If you can introduce a `stop` method to the `Thread` class without the performance penalty, please let me know. I would love to see an optimal cross-platform solution to the problem. – Noctis Skytower Feb 20 '15 at 16:21
  • I don't say, that I can't accept some slowness, but that code is running forever. – Paul R Feb 20 '15 at 16:23
  • @AndrewFount Did you check [this question](http://stackoverflow.com/questions/323972)? The `ThreadWithExc` class listed in one of the answers appears to not have any overhead whatsoever. A little magic with `ctypes` is all that seems to be required. – Noctis Skytower Feb 20 '15 at 16:29
  • This is exactly the function that implemented now. Alas, it is not always kills the thread. – Paul R Feb 20 '15 at 16:31
  • @AndrewFount It sounds like you are running some code (maybe compiled from C) that takes a long time to return back to the interpreter. Since you said `multiprocessing` is not an option, I am guessing that this [timeout.py](https://code.google.com/p/verse-quiz/source/browse/trunk/timeout.py) module would not help you? If you want to have better control over killing execution, starting another process would probably be a the way to go (if possible). – Noctis Skytower Feb 20 '15 at 16:38
  • @AndrewFount Please see the documentation for [os.popen](https://docs.python.org/3/library/os.html?highlight=popen#os.popen), [subprocess.call](https://docs.python.org/3/library/subprocess.html?highlight=popen#subprocess.call), [subprocess.check_call](https://docs.python.org/3/library/subprocess.html?highlight=popen#subprocess.check_call), [subprocess.check_output](https://docs.python.org/3/library/subprocess.html?highlight=popen#subprocess.check_output), and [subprocess.Popen](https://docs.python.org/3/library/subprocess.html?highlight=popen#subprocess.Popen). One of those should work for you – Noctis Skytower Feb 20 '15 at 16:50
  • How can subprocess invoke a function? – Paul R Feb 20 '15 at 17:19
  • @AndrewFount There are at least two different ways that subprocess can invoke a function: (1) move your function into a separate file, execute the file with python, and handle any needed IO or (2) modify your current program to execute itself and again handle IO between the two running instances. When starting up, your program could check for several things to determine if it only needs to run your function: (1) a temporary file with arguments to process, (2) the command line with you arguments provided there, or (3) environment variables noting only your specific function needs to be run. – Noctis Skytower Feb 20 '15 at 17:30
  • Is you code stops thread immediately or not? I tested it and it appears that thread is stopped after some time. – Paul R Feb 20 '15 at 23:36
  • @AndrewFount My code should stop the thread when the currently executing operation (interpreter byte-code) finishes executing. – Noctis Skytower Feb 21 '15 at 12:52
0

you can perform terminal commands using os.system this will allow you to have terminal control from within the program. if you want to

import os
os.system("terminal command goes here")

for linux read up on this: https://www.digitalocean.com/community/tutorials/how-to-use-ps-kill-and-nice-to-manage-processes-in-linux

look into pthread_kill() Stack exchange - how to kill a thtread

for windows: http://forum.thewindowsclub.com/windows-tips-tutorials-articles/29463-kill-processes-using-command-prompt-windows-7-8-a.html

for mac: http://www.chriswrites.com/how-to-view-and-kill-processes-using-the-terminal-in-mac-os-x/

ALTERNATIVELY:

import signal
signum = signal.getsignal(c)
signal.siginterrupt(signum, flag)

OR:

import os
sigID = c.get_ident 
os.system("kill -SIGKILL " + sigID)

signal doc page there is also some timer functions you can set as well.

additional info on python threading: https://docs.python.org/2/library/thread.html

Community
  • 1
  • 1
castaway2000
  • 306
  • 5
  • 21
  • can you provide a small snippet for linux? I don't know how to get thread id and what command to use in linux? – Paul R Feb 20 '15 at 16:25
  • are you needing the thread of your own program or an external program? – castaway2000 Feb 20 '15 at 16:29
  • `pthread_kill()` [http://linux.die.net/man/3/pthread_kill](http://linux.die.net/man/3/pthread_kill) – castaway2000 Feb 20 '15 at 16:30
  • also it might be worth to checkout this thread here: [http://unix.stackexchange.com/questions/1066/how-can-i-kill-a-particular-thread-of-a-process](http://unix.stackexchange.com/questions/1066/how-can-i-kill-a-particular-thread-of-a-process) – castaway2000 Feb 20 '15 at 16:32
  • you want to use `pidof` from the terminal [pidof man page](http://linux.die.net/man/8/pidof) – castaway2000 Feb 20 '15 at 16:40
  • I meant from python. If I have thread c, how to stop it using os.system? – Paul R Feb 20 '15 at 16:43
  • `import signal` `signal.siginterrupt(c, flag)` [singal doc](https://docs.python.org/2/library/signal.html) in this doc there is also a timer function that might be of use to you as well. – castaway2000 Feb 20 '15 at 16:49
  • please see my code snipet i added in my answer under **alternatively** – castaway2000 Feb 20 '15 at 16:57
  • are you sure it 100% in every way does not work? can you check and see if you can retrieve the signum? what is the expected output of **c** in your code? if you can get signum you can run it with `os.system("kill -SIGKILL "+signum)` and kill it. – castaway2000 Feb 20 '15 at 17:07
  • another possibility is `sigID = c.get_ident` `os.system("kill -SIGKILL " + sigID)` something to note is thread ID's are recycled so you want to be careful to not call the same thread id over and over and that you re-get the id each time you use it as a variable. – castaway2000 Feb 20 '15 at 17:13
  • if this does not work i am sorry to say but i am out of ideas. – castaway2000 Feb 20 '15 at 17:14
  • if it works i would appreciate the check-mark next to my answer otherwise good luck I am interested to know either way! – castaway2000 Feb 20 '15 at 17:21
  • well I don't think i can be much more of a help. I am going to delete my answer if the information here is not helpful to this question as it will only mislead the next person. if you want these for notes i will leave this info up till i get home from work tonight. – castaway2000 Feb 21 '15 at 00:36
  • I'm sure it is helpful, but not in my situation as I use mod_wsgi. – Paul R Feb 21 '15 at 09:06