Изящно завершающие потоки Python
Я пытаюсь написать клиентскую программу unix, которая слушает сокет, stdin и чтение из файловых дескрипторов. Я назначаю каждую из этих задач отдельному потоку и успешно связываю их с "основным" приложением, используя синхронизированные очереди и семафор. Проблема в том, что когда я хочу завершить эти дочерние потоки, все они блокируют ввод. Кроме того, потоки не могут регистрировать обработчики сигналов в потоках, потому что в Python разрешен только основной поток выполнения.
Любые предложения?
Ответы
Ответ 1
Нет никакого хорошего способа обойти это, особенно когда поток блокируется.
У меня была аналогичная проблема (Python: как прервать блокирующий поток), и единственный способ остановить мои потоки - закрыть базовое соединение. Это привело к потоку, который блокировал повышение и исключение, а затем разрешил мне проверить флаг остановки и закрыть.
Пример кода:
class Example(object):
def __init__(self):
self.stop = threading.Event()
self.connection = Connection()
self.mythread = Thread(target=self.dowork)
self.mythread.start()
def dowork(self):
while(not self.stop.is_set()):
try:
blockingcall()
except CommunicationException:
pass
def terminate():
self.stop.set()
self.connection.close()
self.mythread.join()
Еще одна вещь, которую следует отметить, - обычно операции блокировки, как правило, предлагают таймаут. Если у вас есть этот вариант, я бы подумал о его использовании. Мой последний комментарий: вы всегда можете установить поток на deamonic,
Из pydoc:
Поток может быть помечен как "поток демона". Значение этого флага заключается в том, что вся программа Python завершается, когда остаются только потоки демона. Начальное значение наследуется от создающего потока. Флаг может быть установлен через свойство демона.
Ответ 2
Кроме того, потоки не могут регистрировать обработчики сигналов
Сигналы для уничтожения потоков потенциально ужасны, особенно в C, особенно если вы выделяете память как часть потока, так как она не будет освобождена, когда этот конкретный поток погибнет (как и в куче процесса). В C нет сборки мусора, поэтому, если этот указатель выходит за пределы области видимости, он выходит из области видимости, память остается выделенной. Поэтому просто будьте осторожны с этим - сделайте это только в C, если вы собираетесь фактически убить все потоки и завершить процесс, чтобы память была возвращена в ОС - добавление и удаление потоков из потокового пула, например приведет к утечке памяти.
Проблема в том, что когда я хочу завершить эти дочерние потоки, они все блокируют ввод.
Как ни странно, я недавно сражался с тем же. Решение буквально не делает блокировки вызовов без тайм-аута. Итак, например, вы хотите идеально:
def threadfunc(running):
while running:
blockingcall(timeout=1)
где бег передается из управляющего потока - я никогда не использовал потоки, но я использовал многопроцессорность, и с этим вам действительно нужно передать объект Event()
и проверить is_set()
. Но вы просили шаблоны проектирования, что основная идея.
Затем, когда вы хотите, чтобы этот поток заканчивался, вы запускаете:
running.clear()
mythread.join()
и ваш основной поток должен позволить вашему потоку клиента обрабатывать свой последний вызов и возвращать, и вся программа прекрасно складывается.
Что вы делаете, если у вас есть блокирующий вызов без тайм-аута? Используйте асинхронную опцию и сон (как при вызове любого метода вы должны приостановить поток в течение определенного периода времени, чтобы вы не вращались), если вам нужно. Другого пути нет.
Ответ 3
См. ответы:
Python SocketServer
Как выйти из многопоточной программы?
В принципе, не блокируйте recv()
с помощью select()
с таймаутом для проверки удобочитаемости сокета и опробуйте флаг quit, когда select()
истечет время ожидания.