Terminate a multi-thread python program

前端 未结 7 755
猫巷女王i
猫巷女王i 2020-12-02 16:17

How to make a multi-thread python program response to Ctrl+C key event?

Edit: The code is like this:

import threading
current = 0

c         


        
相关标签:
7条回答
  • 2020-12-02 16:53

    There're two main ways, one clean and one easy.

    The clean way is to catch KeyboardInterrupt in your main thread, and set a flag your background threads can check so they know to exit; here's a simple/slightly-messy version using a global:

    exitapp = False
    if __name__ == '__main__':
        try:
            main()
        except KeyboardInterrupt:
            exitapp = True
            raise
    
    def threadCode(...):
        while not exitapp:
            # do work here, watch for exitapp to be True
    

    The messy but easy way is to catch KeyboardInterrupt and call os._exit(), which terminates all threads immediately.

    0 讨论(0)
  • 2020-12-02 16:54

    I would rather go with the code proposed in this blog post:

    def main(args):
    
        threads = []
        for i in range(10):
            t = Worker()
            threads.append(t)
            t.start()
    
        while len(threads) > 0:
            try:
                # Join all threads using a timeout so it doesn't block
                # Filter out threads which have been joined or are None
                threads = [t.join(1000) for t in threads if t is not None and t.isAlive()]
            except KeyboardInterrupt:
                print "Ctrl-c received! Sending kill to threads..."
                for t in threads:
                    t.kill_received = True
    

    What I have changed is the t.join from t.join(1) to t.join(1000). The actual number of seconds does not matter, unless you specify a timeout number, the main thread will stay responsive to Ctrl+C. The except on KeyboardInterrupt makes the signal handling more explicit.

    0 讨论(0)
  • 2020-12-02 16:55

    Make every thread except the main one a daemon (t.daemon = True in 2.6 or better, t.setDaemon(True) in 2.6 or less, for every thread object t before you start it). That way, when the main thread receives the KeyboardInterrupt, if it doesn't catch it or catches it but decided to terminate anyway, the whole process will terminate. See the docs.

    edit: having just seen the OP's code (not originally posted) and the claim that "it doesn't work", it appears I have to add...:

    Of course, if you want your main thread to stay responsive (e.g. to control-C), don't mire it into blocking calls, such as joining another thread -- especially not totally useless blocking calls, such as joining daemon threads. For example, just change the final loop in the main thread from the current (utterless and damaging):

    for i in range(0, thread_count):
        threads[i].join()
    

    to something more sensible like:

    while threading.active_count() > 0:
        time.sleep(0.1)
    

    if your main has nothing better to do than either for all threads to terminate on their own, or for a control-C (or other signal) to be received.

    Of course, there are many other usable patterns if you'd rather have your threads not terminate abruptly (as daemonic threads may) -- unless they, too, are mired forever in unconditionally-blocking calls, deadlocks, and the like;-).

    0 讨论(0)
  • 2020-12-02 16:57
    thread1 = threading.Thread(target=your_procedure, args = (arg_1, arg_2))    
    try:
          thread1.setDaemon(True)  # very important
          thread1.start()
    except (KeyboardInterrupt, SystemExit):
          cleanup_stop_thread()
          sys.exit()
    

    When you want to kill the thread just use:

    thread1.join(0)
    
    0 讨论(0)
  • 2020-12-02 17:02

    You can always set your threads to "daemon" threads like:

    t.daemon = True
    t.start()
    

    And whenever the main thread dies all threads will die with it.

    http://www.regexprn.com/2010/05/killing-multithreaded-python-programs.html

    0 讨论(0)
  • 2020-12-02 17:07

    A Worker might be helpful for you:

    #!/usr/bin/env python
    
    import sys, time
    from threading import *
    from collections import deque
    
    class Worker(object):
        def __init__(self, concurrent=1):
            self.concurrent = concurrent
            self.queue = deque([])
            self.threads = []
            self.keep_interrupt = False
    
        def _retain_threads(self):
            while len(self.threads) < self.concurrent:
                t = Thread(target=self._run, args=[self])
                t.setDaemon(True)
                t.start()
                self.threads.append(t)
    
    
        def _run(self, *args):
            while self.queue and not self.keep_interrupt:
                func, args, kargs = self.queue.popleft()
                func(*args, **kargs)
    
        def add_task(self, func, *args, **kargs):
            self.queue.append((func, args, kargs))
    
        def start(self, block=False):
            self._retain_threads()
    
            if block:
                try:
                    while self.threads:
                        self.threads = [t.join(1) or t for t in self.threads if t.isAlive()]
                        if self.queue:
                            self._retain_threads()
                except KeyboardInterrupt:
                    self.keep_interrupt = True
                    print "alive threads: %d; outstanding tasks: %d" % (len(self.threads), len(self.queue))
                    print "terminating..."
    
    
    # example
    print "starting..."
    worker = Worker(concurrent=50)
    
    def do_work():
        print "item %d done." % len(items)
        time.sleep(3)
    
    def main():
        for i in xrange(1000):
            worker.add_task(do_work)
        worker.start(True)
    
    main()
    print "done."
    
    # to keep shell alive
    sys.stdin.readlines()
    
    0 讨论(0)
提交回复
热议问题