Самый питонский способ убить нить через некоторое время
Я хотел бы запустить процесс в потоке (который перебирает большую таблицу базы данных). Пока поток работает, я просто хочу, чтобы программа ждала. Если этот поток занимает больше 30 секунд, я хочу убить поток и сделать что-то еще. Убивая поток, я имею в виду, что я хочу, чтобы он прекратил деятельность и изящно освободил ресурсы.
Я решил, что лучший способ сделать это через Thread()
"s join(delay)
а также is_alive()
функции и Event
, С использованием join(delay)
Я могу заставить свою программу ждать 30 секунд, чтобы закончить поток, и с помощью is_alive()
Функция я могу определить, закончил ли поток свою работу. Если он не завершил свою работу, событие устанавливается, и поток знает, что на этом этапе перестать работать.
Является ли этот подход действительным, и является ли это самым питоническим способом решения моей проблемы?
Вот пример кода:
import threading
import time
# The worker loops for about 1 minute adding numbers to a set
# unless the event is set, at which point it breaks the loop and terminates
def worker(e):
data = set()
for i in range(60):
data.add(i)
if not e.isSet():
print "foo"
time.sleep(1)
else:
print "bar"
break
e = threading.Event()
t = threading.Thread(target=worker, args=(e,))
t.start()
# wait 30 seconds for the thread to finish its work
t.join(30)
if t.is_alive():
print "thread is not done, setting event to kill thread."
e.set()
else:
print "thread has already finished."
2 ответа
Использование события в этом случае прекрасно работает в качестве механизма сигнализации и фактически рекомендуется в документах модуля потоков.
Если вы хотите, чтобы ваши потоки корректно останавливались, сделайте их недемоническими и используйте подходящий механизм сигнализации, такой как
Event
,
При проверке завершения потока тайм-ауты почти всегда создают место для ошибки. Поэтому при использовании .join()
с таймаутом для первоначального решения инициировать событие в порядке, окончательная проверка должна быть сделана с использованием .join()
без перерыва.
# wait 30 seconds for the thread to finish its work
t.join(30)
if t.is_alive():
print "thread is not done, setting event to kill thread."
e.set()
# The thread can still be running at this point. For example, if the
# thread's call to isSet() returns right before this call to set(), then
# the thread will still perform the full 1 second sleep and the rest of
# the loop before finally stopping.
else:
print "thread has already finished."
# Thread can still be alive at this point. Do another join without a timeout
# to verify thread shutdown.
t.join()
Это может быть упрощено до чего-то вроде этого:
# Wait for at most 30 seconds for the thread to complete.
t.join(30)
# Always signal the event. Whether the thread has already finished or not,
# the result will be the same.
e.set()
# Now join without a timeout knowing that the thread is either already
# finished or will finish "soon."
t.join()
Я опаздываю к этой игре, но я боролся с подобным вопросом, и следующее, кажется, как решило проблему для меня идеально, так и позволило мне выполнить некоторую базовую проверку и очистку состояния потока при выходе из демонизированного подпотока:
import threading
import time
import atexit
def do_work():
i = 0
@atexit.register
def goodbye():
print ("'CLEANLY' kill sub-thread with value: %s [THREAD: %s]" %
(i, threading.currentThread().ident))
while True:
print i
i += 1
time.sleep(1)
t = threading.Thread(target=do_work)
t.daemon = True
t.start()
def after_timeout():
print "KILL MAIN THREAD: %s" % threading.currentThread().ident
raise SystemExit
threading.Timer(2, after_timeout).start()
Урожайность:
0
1
KILL MAIN THREAD: 140013208254208
'CLEANLY' kill sub-thread with value: 2 [THREAD: 140013674317568]
Я также изо всех сил пытался закрыть поток, ожидавший получения уведомления. Пробное решение, данное здесь пользователем 5737269, но оно не сработало для меня. Он застревал во втором операторе соединения (без тайм-аута). Много боролся, но не нашел решения этой проблемы. Получил это решение, немного подумав: мой поток ожидает получения сообщения в очереди. Я хочу закрыть эту ветку, если в течение 20 секунд не приходит уведомление. Итак, через 20 секунд я пишу сообщение в эту очередь, чтобы поток завершился сам по себе. Вот код:
q = Queue.Queue()
t.join(20)
if t.is_alive():
print("STOPPING THIS THREAD ....")
q.put("NO NOTIFICATION RECEIVED")
t.join(20)
else:
print("Thread completed successfully!!")
У меня это сработало.. Надеюсь, эта идея кому-то поможет!