如何对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
答案 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)