Я работаю с сокетами, используя Python 3.5 и модуль multiprocessing, и столкнулся с неожиданным поведением. Моя программа, по сути, представляет собой пару потоков, выполняющихся до тех пор, пока клиент не получит определенное сообщение о необходимости остановки.
Если я выполню это так, я получу BrokenPipeError, который прерывает программу, несмотря на цикл while True и блок except.
#!/usr/bin/env python3
import signal
import traceback
from multiprocessing.dummy import Pool
from process import Process
def main():
while True:
try:
# a Process has a couple threads
process = Process()
listener_pool = Pool(processes=1)
sender_pool = Pool(processes=1)
# blocking socket client that runs forever
listener_pool.apply_async(process.listener_thread.run, ())
# blocking socket server that runs forever
sender_pool.apply_async(process.sender_thread.run, ())
return 0
except BrokenPipeError:
traceback.print_exc()
# closing connections
process.emitter_thread.socket.close()
process.listener_thread.socket.close()
if __name__ == '__main__':
main()
Однако, когда я добавил signal.pause() прямо перед возвратом, не только код работал так, как задумано, но и BrokenPipeError не вызывается в любое время.
#!/usr/bin/env python3
import signal
import traceback
from multiprocessing.dummy import Pool
from process import Process
def main():
while True:
try:
# a Process has a couple threads
process = Process()
listener_pool = Pool(processes=1)
sender_pool = Pool(processes=1)
# blocking socket client that runs forever
listener_pool.apply_async(process.listener_thread.run, ())
# blocking socket server that runs forever
sender_pool.apply_async(process.sender_thread.run, ())
signal.pause()
return 0
except BrokenPipeError:
traceback.print_exc()
# closing connections
process.emitter_thread.socket.close()
process.listener_thread.socket.close()
if __name__ == '__main__':
main()
Согласно документы, сигналы могут обрабатываться только основным потоком, но оба сокета обрабатываются вторичными потоками. Что делает signal.pause() для предотвращения поломки сокетов, когда они даже не вызываются в одном контексте?






apply_async, как следует из названия, возвращается немедленно. Поэтому в первой версии вашей программы основной поток завершается сразу после запуска дочерних потоков (фиктивные процессы - это просто потоки, использующие многопроцессорный API).
Следовательно, основной поток вышел из блока try задолго до возникновения исключения.
Во втором случае signal.pause() заставляет основной поток ждать в блоке try, где он перехватит исключение.
Обратите внимание, что это хрупкое решение, поскольку получение сигнала любой приведет к возобновлению работы signal.pause() и выходу из основного потока.
Спасибо за ответ. Но тогда разве traceback.print_exc() не предупредит меня, что сокет сломан, вместо того, чтобы тихо выйти из строя? Я убрал код, чтобы его было легче читать, но оба потока по крайней мере записывали критические исключения в файл журнала. Я даже переместил signal.pause();return 0 ниже блока except, и у меня все еще нет BrokenPipeError. Означает ли это, что никакие сокеты не выходят из строя?
Это, вероятно, означает, что они не терпят неудачу. Конечно, вы можете попробовать сделать одну ошибку намеренно, вы должны увидеть исключение.
Просто попробовал. Они действительно не терпят неудач. Я поигрался с заменой signal.pause() with time.sleep () `и считаю, что основной поток после выхода закрыл все соединения (возможно, как часть сборки мусора). Спасибо за помощь, теперь я понимаю, что здесь нужно исправить.
Кстати, это ваше дизайнерское решение, но если вы работаете с python3.5 +, переход в асинхронный режим намного удобнее, чем потоки Python (будь то потоки или многопроцессорность api).