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