Завершить многопоточной программы на Python
Как сделать многопоточный ответ программы python на ключевое событие Ctrl+C?
Edit: код выглядит так:
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() каждого потока?
Edit: приведенный выше код должен работать, но он всегда прерывается, когда текущая переменная была в 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 ответов:
сделать каждый поток, кроме основного демона (
t.daemon = True
в 2.6 или лучше,t.setDaemon(True)
в 2.6 или меньше, для каждого объекта потокаt
прежде чем приступить к ней). Таким образом, когда основной поток получает KeyboardInterrupt, если он не поймает его или не поймает, но все равно решит завершить, весь процесс завершится. Смотрите документы.edit: только что увидел код OP (первоначально не опубликованный) и утверждение, что "он не работает", кажется, я должен добавить...:
конечно, если вы хотите, чтобы ваш основной поток оставался отзывчивым (например, для управления-C), не мешайте ему блокировать вызовы, такие как
join
Инг другой поток, особенно не полностью бесполезно блокировка вызовов, таких какjoin
ing демон потоки. Например, просто измените конечную петлю в главном потоке от текущей (полной и повреждающей):for i in range(0, thread_count): threads[i].join()
к чему-то более разумному например:
while threading.active_count() > 0: time.sleep(0.1)
если ваш main не имеет ничего лучшего, чем либо для всех потоков, чтобы закончить самостоятельно, или для управления-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 и вызвать ОС._exit (), который немедленно завершает все потоки.
Я бы предпочел пойти с кодом, предложенным в этот блог:
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). Фактическое количество секунд не имеет значения, если вы не укажете номер тайм-аута, основной поток будет реагировать на Ctrl+C. Кроме on KeyboardInterrupt делает обработку сигнала более четким.
A работник может быть полезен для вас:
#!/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()
вы всегда можете установить свои потоки в" демон " потоки, такие как:
t.daemon = True t.start()
и всякий раз, когда основной поток умирает все потоки умрут с ним.
http://www.regexprn.com/2010/05/killing-multithreaded-python-programs.html
если вы создаете нить, как так -
myThread = Thread(target = function)
- а потом делатьmyThread.start(); myThread.join()
. Когда CTRL-C инициируется, основной поток не выходит, потому что он ждет этой блокировкиmyThread.join()
звонок. Чтобы исправить это, просто положить в тайм-аут на .присоединяйтесь к() вызова. Тайм-аут может быть так долго, как вы хотите. Если вы хотите, чтобы он ждал бесконечно, просто введите очень длинный тайм-аут, например 99999. Это также хорошая практика, чтобы сделатьmyThread.daemon = True
таким образом, все потоки выходят, когда основной поток(не-демон) выходит.