终止多线程python程序

时间:2009-10-28 03:41:21

标签: python multithreading

如何对Ctrl + C键事件进行多线程python程序响应?

编辑:代码如下:

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

我试图在所有线程上删除join()但它仍然无效。是因为每个线程的run()过程中的锁段?

编辑:上述代码可以正常工作,但当当前变量在5,000-6,000范围内并且通过以下错误时它总是被中断

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 个答案:

答案 0 :(得分:89)

在开始之前,为每个线程对象t.daemon = True创建除主要节点之外的每个线程(2.6 {或更高版本中的t.setDaemon(True),2.6或更低版本中的t。这样,当主线程接收到KeyboardInterrupt时,如果它没有捕获它或捕获它但决定终止,整个过程将终止。请参阅the docs

编辑:刚刚看到OP的代码(最初没有发布)以及“它​​不起作用”的说法,看来我必须添加......:

当然,如果你希望你的主线程能够保持响应(例如控制-C),不要让它陷入阻塞调用,例如join另一个线程 - 尤其不是完全无用的阻止调用,例如join 守护程序线程。例如,只需更改主线程中的最终循环(无语和破坏):

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

更合情合理的事情:

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

如果你的主要没有比所有线程自己终止或者接收控制-C(或其他信号)更好的事情。

当然,还有许多其他可用的模式,如果你宁愿让你的线程不会突然终止(作为守护线程可能) - 除非他们也会无条件地阻塞呼叫,死锁等; - )。

答案 1 :(得分:16)

有两种主要方式,一种清洁,一种容易。

干净的方法是在你的主线程中捕获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

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

答案 2 :(得分:5)

工作人员 可能对您有所帮助:

#!/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()

答案 3 :(得分:4)

我更愿意使用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

我所改变的是从 t.join(1) t.join(1000) t.join 。实际的秒数无关紧要,除非您指定超时数,主线程将保持对Ctrl + C的响应。 KeyboardInterrupt 上的除外使信号处理更加明确。

答案 4 :(得分:4)

您始终可以将线程设置为“守护程序”线程,如:

t.daemon = True
t.start()

每当主线程死亡时,所有线程都会死掉。

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

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

当你想杀死线程时,只需使用:

thread1.join(0)