@Statium

Как реализовать остановку цикла for?

Добрый день, имеется скрипт, код ниже:
class HelloWorldSvc(win32serviceutil.ServiceFramework):
    _svc_name_ = "Monitoring"
    _svc_display_name_ = "HMonitoring"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.stop_event = win32event.CreateEvent(None, 0, 0, None)
        socket.setdefaulttimeout(60)
        self.stop_requested = False

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.stop_event)
        self.stop_requested = True

    def SvcDoRun(self):
        servicemanager.LogMsg(
            servicemanager.EVENTLOG_INFORMATION_TYPE,
            servicemanager.PYS_SERVICE_STARTED,
            (self._svc_name_, '')
        )
        self.main()

    def main(self):
        # Simulate a main loop
        time.sleep(5)
        for i in iter(int, 1):
            # Подключение к MySQL
            logging.info("Подключаемся к БД MySQL")
            mydb = mysql.connector.connect(host/pass)
            mycursor = mydb.cursor()
            logging.info("Подключение выполнено успешно")

            time.sleep(3)
            if self.stop_requested:
                logging.info('Получен запрос на остановку службы. Служба остановлена')
                break

            # Получаем IP-адрес сервера
            logging.info("Запрашиваем IP-адрес сервера")
            try:
                ip = requests.get('http://localhost/')  # get-запрос
                logging.info("IP-адрес получен")
                time.sleep(3)
                ip = ip.text
            except:
                ip = "Error"
                logging.info("Не могу получить IP-адрес")

            if self.stop_requested:
                logging.info('Получен запрос на остановку службы. Служба остановлена')
                break
        return


if __name__ == '__main__':
    if len(sys.argv) == 1:
        servicemanager.Initialize()
        servicemanager.PrepareToHostSingle(HelloWorldSvc)
        servicemanager.StartServiceCtrlDispatcher()
    else:
        win32serviceutil.HandleCommandLine(HelloWorldSvc)


Данный код копилируется в exe и запускается в винде как служба, это только кусочек кода, на самом деле цикл (бесконечный) выполняет множество различных запросов и отправляет данные на сервер.

После каждого запроса у меня в коде стоит
if self.stop_requested:
                logging.info('Получен запрос на остановку службы. Служба остановлена')
                break

Если попытаться остановить службу она останавливает цикл. Дак вот у меня этих кусков кода с проверкой остановки, в скрипте штук 50. Можно ли как то остановить цикл сразу после остановки службы? Если оставить этот if в одном месте, перед остановкой служба ждет завершение всего цикла.
  • Вопрос задан
  • 158 просмотров
Решения вопроса 1
Vindicar
@Vindicar
RTFM!
Если тебе нужна проверка на останов после каждой операции - придётся-таки эту проверку производить после каждой операции. Да, можно схитрить, но от этого не уйдёшь.

Я могу посоветовать убрать time.sleep() и заменить их на threading.Event.wait() с таймаутом - в этом случае, если Event будет взведено, то не придётся "досыпать". Но проверять исход ожидания всё равно придётся самому.

Альтернативное, но ОЧЕНЬ кардинальное решение - сделать код асинхронным. Причина в том, что у асинхронных подпрограмм, завёрнутых в Task, есть механизм отмены, позволяющий принудительно выкинуть исключение в ходе выполнения любой длительной операции (любого await-вызова). По сути, это та же самая проверка - но встроенная.

Это работает, так как асинхронный код выполняется в одном потоке. Провернуть такой же фокус, скажем, в многопоточном приложении - нетривиально и довольно рискованно.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы