Остановка потоков, порожденных BaseHTTPServer с использованием ThreadingMixin
Я прочитал здесь в этом посте, что с помощью ThreadingMixin
(от SocketServer
модуль), вы можете создать многопоточный сервер с BaseHTTPServer
, Я попробовал это, и это работает. Однако как я могу остановить активные потоки, порожденные сервером (например, во время выключения сервера)? Это возможно?
2 ответа
Самое простое решение - просто использовать daemon_threads
, Короткая версия: просто установите для этого True, и не беспокойтесь об этом; Когда вы выйдете, все работающие потоки остановятся автоматически.
Как ThreadingMixIn
документы говорят:
При наследовании от ThreadingMixIn для поведения многопоточного соединения, вы должны явно объявить, как вы хотите, чтобы ваши потоки вели себя при внезапном завершении работы. Класс ThreadingMixIn определяет атрибут daemon_threads, который указывает, должен ли сервер ожидать завершения потока. Вы должны установить флаг явно, если хотите, чтобы потоки работали автономно; по умолчанию используется значение False, означающее, что Python не завершит работу, пока не завершатся все потоки, созданные ThreadingMixIn.
Более подробная информация доступна в threading
документы:
Поток может быть помечен как "поток демона". Значение этого флага заключается в том, что вся программа Python завершается, когда остаются только потоки демона. Начальное значение наследуется от потока создания. Флаг может быть установлен через свойство демона.
Иногда это не подходит, потому что вы хотите завершить работу без выхода, или потому что ваши обработчики могут иметь очистку, которую необходимо выполнить. Но когда это уместно, проще не бывает.
Если все, что вам нужно, это способ завершить работу без выхода и не требует гарантированной очистки, вы можете использовать API-интерфейсы для отмены потоков для конкретной платформы через ctypes
или же win32api
, Обычно это плохая идея, но иногда это то, что вы хотите.
Если вам нужно чистое отключение, вам нужно создать собственный механизм для того, чтобы потоки взаимодействовали. Например, вы можете создать глобальную переменную "флаг выхода", защищенную threading.Condition
и ваш handle
Функция проверять это периодически.
Это замечательно, если потоки просто выполняют медленную, неблокирующую работу, которую можно разбить на более мелкие части. Например, если handle
Функция всегда проверяет флаг выхода по крайней мере один раз каждые 5 секунд, вы можете гарантировать возможность отключения потоков в течение 5 секунд. Но что, если потоки блокируют работу - как, вероятно, и есть, потому что по всей причине вы использовали ThreadingMixIn
должен был позволить вам делать звонки блокировки вместо того, чтобы писать select
петли или использование asyncore
или т.п?
Ну, нет хорошего ответа. Очевидно, что если вам просто нужно, чтобы отключение произошло "в конце концов", а не "в течение 5 секунд" (или если вы хотите отказаться от чистого отключения через 5 секунд и вернуться либо к использованию API-интерфейсов, специфичных для платформы, либо к демонизации потоков), вы можно просто поставить чеки до и после каждого вызова блокировки, и это будет "часто" работать. Но если этого недостаточно, вы ничего не сможете сделать.
Если вам это нужно, лучший ответ - изменить архитектуру, чтобы использовать платформу, в которой есть способы сделать это. Самые популярные варианты Twisted
, Tornado
, а также gevent
, В будущем PEP 3156 принесет аналогичную функциональность в стандартную библиотеку, а также будет частично полная справочная реализация tulip
с этим стоит поиграть, если вы не пытаетесь создать что-то для реального мира, которое должно быть готово в ближайшее время.
Вот пример кода, показывающий, как использовать threading.Event для выключения сервера при любом запросе POST,
import SocketServer
import BaseHTTPServer
import threading
quit_event = threading.Event()
class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""This handler fires the quit event on POST."""
def do_GET(self):
self.send_response(200)
def do_POST(self):
quit_event.set()
self.send_response(200)
class MyThreadingHTTPServer(
SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
server = MyThreadingHTTPServer(('', 8080), MyRequestHandler)
threading.Thread(target=server.serve_forever).start()
quit_event.wait()
server.shutdown()
Сервер полностью отключен, поэтому вы можете сразу же перезапустить сервер, и порт будет доступен вместо того, чтобы получить "Адрес уже используется".