终止多线程 Python 程序

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

编辑: 代码如下:

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 ()过程中的锁段吗?

编辑: 上面的代码应该可以工作,但是当当前变量在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
91950 次浏览

除了主线程之外,每个线程都是守护进程(对于启动之前的每个线程对象 tt.daemon = True在2.6以上,t.setDaemon(True)在2.6以下)。这样,当主线程接收到 KeyboardInterrupt 时,如果它没有捕获到它或捕获到它,但是决定终止,那么整个进程将终止。见 那些文件

编辑 : 刚刚看到 OP 的代码(没有最初发布)和“它不工作”的声明,似乎我必须添加... :

当然,如果您希望您的主线程保持响应性(例如,对 control-C) ,不要让它陷入阻塞调用的泥潭,例如 joining 另一个线程——特别是不要完全使用 没用阻塞调用,例如 joining Daemon线程。例如,只需将主线程中的最后一个循环从电流(无声且有害)更改为:

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

变得更加明智,比如:

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

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

当然,如果您希望线程不会突然终止,还有许多其他可用的模式(就像守护线程可能的情况一样)——除非 他们也永远陷入无条件阻塞调用、死锁等等的泥潭; ——)。

有两种主要的方法,一种是干净的,一种是容易的。

干净的方法是在主线程中捕获 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 () ,它会立即终止所有线程。

工人可能对你有帮助:

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

我更愿意采用 这篇博文中提出的代码:

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 加入T.join (1)T.join (1000)。实际的秒数并不重要,除非您指定一个超时数,否则主线程将保持对 Ctrl + C 的响应。键盘中断上的除外器使得信号处理更加明确。

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

您总是可以将线程设置为“守护”线程,比如:

t.daemon = True
t.start()

每当主线死亡,所有的线程都会随之死亡。

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

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)