Самый питонский способ убить нить через некоторое время

Я хотел бы запустить процесс в потоке (который перебирает большую таблицу базы данных). Пока поток работает, я просто хочу, чтобы программа ждала. Если этот поток занимает больше 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!!")

У меня это сработало.. Надеюсь, эта идея кому-то поможет!

Другие вопросы по тегам