Всем доброго времени суток!
Разобрался я в этой проблеме. Имя ей - GIL. Более подробное описание наших изысканий:
Есть некая программа, которая должна одновременно выполнять 2-е функции: Собирать некие данные и общаться с клиентами(т е передавать им данные). Т к недопустимо, чтобы клиенты ждали конца сбора данных, то логично выделать 2-е разные нити под эти процессы - тогда сбор данных и общение с клиентами будет вестись параллельно. обращаю ваше внимание на один момент - НЕЛЬЗЯ предсказать, когда каждая из этих нитей завершится. Теоретически они могут работать бесконечно. Практически же - до тех пор, пока администратор не остановит.
Первое решение которое приходит в голову - в этих потоках запускаем бесконечный цикл с проверкой флага:
flag_stop = False
while not flag_stop:
time.sleep(1.0)
потом связать flag_stop = True с нужным сигналом(например SIGINT). По сигналу флаг сменится и цикл остановится. Но не все так просто. Данная тема началась с того, что это как раз таки не работает. После того как исполняем threading.Thread.join() сигналы перестают приниматься. А происходит это потому, что в join() вызывается блокировка ( thread.allocate_lock() ). После этого потоки начинают варится в собственном соку и достучаться до них снаружи - не получится. Как я понимаю это происходит из-за GIL. После некоторого гуления нашел следующий траблтикет:
Issue1167930, в котором обсуждается аналогичная проблема. Но увы - обсуждение быстро закончилось следующим сообщением Гвидо:
Guido van Rossum
This is because the regular acquire() method on a basic lock cannot be interrupted. That's unlikely to go away, so you'll just have to live with this. As you've discovered, specifying a timeout solves the issue (sort of).
А что это он говорил про timeout? Изучив документацию видим, что в join можно передать некоторую задержку. А что же она нам дает? Дальше начинаются мои собственные изыскания - опытным путем было установлено, что блокировка не наступает, до тех пор, пока не истечет задержка. Т е передали 10 - в течении 10 сек можно передать сигнал и он сработает как планировалось. Но после - увы - все по прежнему. Т к предсказать интервал, за который отработают процессы я не могу - он бесконечен, то этот путь нам не подходит.
Второе решение - переписать join без lock я пока оставляю без внимания - именно так и сделал ZZZ, но это породило другие проблемы. Видимо не зря там эта блокировка стоит. Тем более что Гвидо написал, что с этим придется жить - тоже наверно не зря…
Третье решение - раз гора не идет к Магомеду, то Магомед пойдет к горе. Да - снаружи послать сигнал нельзя, но ведь можно остановить процессы изнутри! Для этого надо сделать там проверку некого условия, которое находится снаружи. На практике это может быть так:создаем некий файлик и пишем туда 0. Процесс каждую итерацию проверяет этот ноль. В нужный нам момент меняем 0 на 1 и процесс, обнаружив эту замену прекращает выполнение. Данный принцип может быть основан не только на файлах, но и на чем-нибудь ином, например сокетах, пайпах и тд. Важен принцип -
Нить завершает сама себя.
Для нашей программы я использовал следующий способ. Т к один из потоков это rpc сервер, то я просто зарегистрировал метод остановки в доступных к вызову функциях. Таким образом для остановки достаточно соединится с нужным сокетом и вызвать нужный метод - 2 строки. Не знаю насколько это грамотно. Но останавливается без проблем.
Пожелания и дополнения приветствуются.