终止一个多线程python程序

时间:2021-12-04 20:41:48

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

如何对Ctrl+C键事件做出多线程的python程序响应?

Edit: The code is like this:

编辑:代码是这样的:

import threading
current = 0

class MyThread(threading.Thread):
    def __init__(self, total):
        threading.Thread.__init__(self)
        self.total = total

    def stop(self):
        self._Thread__stop()

    def run(self):
        global current
        while current<self.total:
            lock = threading.Lock()
            lock.acquire()
            current+=1
            lock.release()
            print current

if __name__=='__main__':

    threads = []
    thread_count = 10
    total = 10000
    for i in range(0, thread_count):
        t = MyThread(total)
        t.setDaemon(True)
        threads.append(t)
    for i in range(0, thread_count):
        threads[i].start()

I tried to remove join() on all threads but it still doesn't work. Is it because the lock segment inside each thread's run() procedure?

我试图删除所有线程上的join(),但它仍然不起作用。是因为每个线程的run()过程中的锁段吗?

Edit: The above code is supposed to work but it always interrupted when current variable was in 5,000-6,000 range and through out the errors as below

编辑:上面的代码应该可以工作,但是当当前变量在5000 - 6000范围内并且通过下面的错误时,它总是被中断

Exception in thread Thread-4 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib/python2.5/threading.py", line 486, in __bootstrap_inner
  File "test.py", line 20, in run
<type 'exceptions.TypeError'>: unsupported operand type(s) for +=: 'NoneType' and 'int'
Exception in thread Thread-2 (most likely raised during interpreter shutdown):
Traceback (most recent call last):
  File "/usr/lib/python2.5/threading.py", line 486, in __bootstrap_inner
  File "test.py", line 22, in run

7 个解决方案

#1


86  

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.

使除主线程之外的所有线程都成为守护进程(t)。守护进程在2.6中为真,在2.6中为真,在2.6中为真,在开始之前为每个线程对象t)。这样,当主线程接收到KeyboardInterrupt时,如果它没有捕获它或捕获它,但无论如何决定终止,整个进程将终止。看文档。

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...:

编辑:刚刚看到OP的代码(不是最初发布的)和“它不工作”的声明,看起来我必须添加…

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):

当然,如果您希望主线程保持响应性(例如control-C),不要将其陷入阻塞调用,例如加入另一个线程——特别是不是完全无用的阻塞调用,比如加入守护进程线程。例如,只需将主线程中的最终循环从当前的(不可言说且具有破坏性)更改为:

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.

如果您的主进程没有什么比让所有线程自己终止或者接收一个control-C(或其他信号)更好的事情了。

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;-).

当然,如果您希望您的线程不会突然终止(就像守护线程可能发生的那样),那么还有许多其他可用的模式——除非它们也永远陷入了无条件阻塞调用、死锁之类的泥潭中;

#2


16  

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:

干净的方法是在主线程中捕获KeyboardInterrupt,并设置背景线程可以检查的标志,以便它们知道退出;这里有一个使用全局变量的简单/稍微有点混乱的版本:

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.

这种混乱但简单的方法是捕获KeyboardInterrupt并调用os._exit(),它立即终止所有线程。

#3


5  

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.

我改变的是t。从。join(1)到。join(1000)。实际的秒数并不重要,除非您指定一个超时号码,否则主线程将保持对Ctrl+C的响应。除了在KeyboardInterrupt之外,还可以使信号处理更显式。

#4


5  

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()

#5


2  

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

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

#6


1  

If you spawn a Thread like so - myThread = Thread(target = function) - and then do myThread.start(); myThread.join(). When CTRL-C is initiated, the main thread doesn't exit because it is waiting on that blocking myThread.join() call. To fix this, simply put in a timeout on the .join() call. The timeout can be as long as you wish. If you want it to wait indefinitely, just put in a really long timeout, like 99999. It's also good practice to do myThread.daemon = True so all the threads exit when the main thread(non-daemon) exits.

如果您生成一个线程,比如:myThread = Thread(target = function),然后执行myThread.start();myThread.join()。当CTRL-C启动时,主线程不会退出,因为它正在等待阻塞myThread.join()调用。要解决这个问题,只需在.join()调用上设置一个超时。超时时间可以随您的意愿而定。如果你想让它无限期地等待,你只需要长时间地等待,比如99999。这也是做myThread的好习惯。守护进程= True,因此当主线程(非守护进程)退出时,所有线程都退出。

#7


0  

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)

#1


86  

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.

使除主线程之外的所有线程都成为守护进程(t)。守护进程在2.6中为真,在2.6中为真,在2.6中为真,在开始之前为每个线程对象t)。这样,当主线程接收到KeyboardInterrupt时,如果它没有捕获它或捕获它,但无论如何决定终止,整个进程将终止。看文档。

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...:

编辑:刚刚看到OP的代码(不是最初发布的)和“它不工作”的声明,看起来我必须添加…

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):

当然,如果您希望主线程保持响应性(例如control-C),不要将其陷入阻塞调用,例如加入另一个线程——特别是不是完全无用的阻塞调用,比如加入守护进程线程。例如,只需将主线程中的最终循环从当前的(不可言说且具有破坏性)更改为:

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.

如果您的主进程没有什么比让所有线程自己终止或者接收一个control-C(或其他信号)更好的事情了。

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;-).

当然,如果您希望您的线程不会突然终止(就像守护线程可能发生的那样),那么还有许多其他可用的模式——除非它们也永远陷入了无条件阻塞调用、死锁之类的泥潭中;

#2


16  

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:

干净的方法是在主线程中捕获KeyboardInterrupt,并设置背景线程可以检查的标志,以便它们知道退出;这里有一个使用全局变量的简单/稍微有点混乱的版本:

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.

这种混乱但简单的方法是捕获KeyboardInterrupt并调用os._exit(),它立即终止所有线程。

#3


5  

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.

我改变的是t。从。join(1)到。join(1000)。实际的秒数并不重要,除非您指定一个超时号码,否则主线程将保持对Ctrl+C的响应。除了在KeyboardInterrupt之外,还可以使信号处理更显式。

#4


5  

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()

#5


2  

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

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

#6


1  

If you spawn a Thread like so - myThread = Thread(target = function) - and then do myThread.start(); myThread.join(). When CTRL-C is initiated, the main thread doesn't exit because it is waiting on that blocking myThread.join() call. To fix this, simply put in a timeout on the .join() call. The timeout can be as long as you wish. If you want it to wait indefinitely, just put in a really long timeout, like 99999. It's also good practice to do myThread.daemon = True so all the threads exit when the main thread(non-daemon) exits.

如果您生成一个线程,比如:myThread = Thread(target = function),然后执行myThread.start();myThread.join()。当CTRL-C启动时,主线程不会退出,因为它正在等待阻塞myThread.join()调用。要解决这个问题,只需在.join()调用上设置一个超时。超时时间可以随您的意愿而定。如果你想让它无限期地等待,你只需要长时间地等待,比如99999。这也是做myThread的好习惯。守护进程= True,因此当主线程(非守护进程)退出时,所有线程都退出。

#7


0  

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)