понимание этого кода многопоточного демона Python

Итак, я новичок в python и работаю над обработчиком событий файловой системы. Я наткнулся на watchdog api и там увидел код многопоточности, который не могу понять.

Вот код, опубликованный на их сайте:

import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    path = sys.argv[1] if len(sys.argv) > 1 else '.'
    event_handler = LoggingEventHandler()
    observer = Observer()
    observer.schedule(event_handler, path, recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

Этот код выполняет бесконечный цикл и прослушивает некоторую папку и записывает то, что видит, в консоль. Мое сомнение находится в нижней части кода.

Итак, вы запускаете наблюдатель. Затем попросите его перейти в бесконечный цикл, пока не будет выполнено какое-либо нажатие клавиши. Я предполагаю, что где-то в коде «observer.start()» они также устанавливают daemon=True. После некоторого нажатия клавиши программа выходит из цикла и останавливает наблюдатель. В API сторожевого таймера определение stop() говорит, что оно останавливает поток демона.

1) Затем он выполняет соединение(). Но зачем нужен этот джойн. Я уже остановил поток демона. Разве join() не означает, что нужно дождаться остановки всех потоков, а затем и только тогда выйти из программы. Могу ли я удалить join() из кода. После того, как я удалил его, моя программа все еще работает правильно.

2) Я также не понимаю необходимости сна (1) внутри цикла while. Что произойдет, если я просто поставлю туда оператор «pass». Я предполагаю, что цикл while будет потреблять больше ресурсов??? И причина в том, что мы установили время сна как 1 секунду, а не 2-3 секунды, потому что в худшем случае пользователю, возможно, придется ждать 2-3 секунды, пока программа закроется. Но я могу ошибаться.


person Rash    schedule 13.10.2014    source источник


Ответы (1)


  1. Помните, что демон работает в процессе родительского процесса. Вам нужно поддерживать родительский процесс во время выполнения этого потока, иначе он будет убит при выходе из программы (и, вероятно, не изящным способом). Это join гарантирует, что процесс останется живым до тех пор, пока все потоки не завершатся; то, что вы вызвали stop, не гарантирует, что поток фактически завершил выполнение. stop – это запрос на остановку потока, он не требует блокировки до тех пор, пока поток не завершится (и не должен этого делать, чтобы родительский поток мог вызывать stop для многих дочерних потоков "одновременно").

  2. Это чисто для снижения потребления процессора. Если бы у вас было просто pass в
    , ЦП выполнял бы этот цикл while как можно быстрее, уменьшая количество циклов. Вызов sleep добровольно уступает центральный процессор другим процессам, поскольку он знает, что ему не нужно будет быстро реагировать на какие-либо конкретные условия. И вы в основном правы, это sleep(1), так что ваше время отклика в худшем случае составляет примерно 1 секунду.

ОБНОВЛЕНИЕ:

Вот пример того, почему важно иметь join. Скажем, в потоке выполнялось следующее:

while not self.stop:  # self.stop is set to True when stop() is called
    ...
    self.results.append(item) # do some stuff that involves appending results to a list
with open('~/output.txt', 'w') as outfile:
    outfile.write('\n'.join(str(item) for item in item))

При вызове stop цикл while завершится, а файл результатов откроется и начнет запись. Если бы join не была вызвана, процесс мог бы завершиться до завершения операции write, что привело бы к повреждению результатов. join гарантирует, что родительский поток ожидает завершения этой записи. Это также гарантирует, что процесс фактически ожидает завершения всей итерации этого цикла while; без join вы можете не только пропустить запись файла, но и завершиться в середине этого блока while.

Однако, если поток, который вызвал stop, не делал ничего продолжительного после завершения while, join эффективно возвращался бы мгновенно и, таким образом, в основном превращается в NOP.

ОБНОВЛЕНИЕ 2:

Что касается вызова sleep, некоторые события (такие как ctrl+c) могут всплывать даже из вызова sleep родительского процесса. Так что в данном конкретном случае продолжительность сна не так уж и важна. Установка его на 1 секунду в основном является просто соглашением, чтобы было ясно, что вы в основном используете «выход ЦП», а не действительно спите.

person aruisdante    schedule 13.10.2014
comment
1) Можно ли с уверенностью сказать, что весь смысл join() здесь в том, что stop() не остановит поток немедленно. Таким образом, если stop() выполняет свою работу быстро и завершает работающий поток демона, то наличие функции join() не имеет значения. Но в тех случаях, когда он не завершается немедленно, join продолжает блокировать основной поток для выхода. Возможно, поскольку я не выполняю никаких интенсивных задач, я всегда видел первый случай и, следовательно, думал, что join() в этом случае непригоден. 2) Вместо 1 я вместо 1 поставил 100. Моя программа работала так же. Почему?? - person Rash; 13.10.2014