终止一个multithreading的python程序
如何使一个multithreadingpython程序响应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()过程中的锁段?
编辑:上面的代码应该工作,但它总是中断当前variables在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
除了main之外的每一个线程都是一个守护进程(对于每个线程对象t
在启动之前, t.daemon = True
2.6或更高, t.setDaemon(True)
在2.6或更低)。 这样,当主线程收到KeyboardInterrupt时,如果它没有捕获它或捕获它,但决定终止,整个过程将终止。 看文档 。
编辑 :刚刚看到OP的代码(不是最初发布)和声称“它不工作”,似乎我必须添加…:
当然,如果你想要你的主线程保持响应(例如控制-C),不要把它们变成阻塞调用,比如join
另一个线程 – 特别是不能完全无用的阻塞调用,例如join
守护线程。 例如,只需从当前(无声的和破坏性的)主线程中更改最后一个循环:
for i in range(0, thread_count): threads[i].join()
到更明智的事情如:
while threading.active_count() > 0: time.sleep(0.1)
如果你的main没有比所有线程自行终止或者接收到control-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.join(1)到t.join(1000)的t.join 。 实际秒数无关紧要,除非您指定了超时值,否则主线程将保持对Ctrl + C的响应。 KeyboardInterrupt除外使信号处理更加明确。
如果你像这样产生一个线程 – myThread = Thread(target = function)
– 然后执行myThread.start(); myThread.join()
myThread.start(); myThread.join()
。 当启动CTRL-C时,主线程不会退出,因为它正在等待阻塞myThread.join()
调用。 要解决这个问题,只需在.join()调用中放置一个超时。 超时可以只要你想。 如果你想要无限期的等待,只需要input一个很长的超时时间,比如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)