Прерывание Python raw_input () в дочернем потоке с помощью ^ C / KeyboardInterrupt - PullRequest
8 голосов
/ 14 февраля 2012

В многопоточной программе на Python один поток иногда запрашивает консольный ввод, используя встроенную raw_input () . Я хотел бы иметь возможность закрыть программу, находясь в приглашении raw_input, набрав ^ C в оболочке (т.е. с сигналом SIGINT). Однако, когда дочерний поток выполняет raw_input, ввод ^ C ничего не делает - KeyboardInterrupt не вызывается, пока я не нажму return (оставив raw_input).

Например, в следующей программе:

import threading

class T(threading.Thread):
    def run(self):
        x = raw_input()
        print x

if __name__ == '__main__':
    t = T()
    t.start()
    t.join()

Ввод ^ C ничего не делает до завершения ввода. Однако, если мы просто вызываем T().run() (т.е. однопоточный случай: просто запустите raw_input в главном потоке), ^ ​​C немедленно закроет программу.

Предположительно, это связано с тем, что SIGINT отправляется в основной поток, который приостанавливается (ожидает GIL), в то время как разветвленный поток блоков на консоли считывается. Основной поток не может выполнить свой обработчик сигнала, пока не получит GIL после возврата raw_input. (Пожалуйста, исправьте меня, если я ошибаюсь по этому поводу - я не эксперт по реализации потоков Python.)

Есть ли способ чтения из stdin в стиле raw_input, позволяющий обрабатывать SIGINT основным потоком и, таким образом, останавливать весь процесс?

[Я наблюдал поведение выше в Mac OS X и нескольких разных Linux.]


Редактировать: Я неправильно описал основную проблему выше. При дальнейшем изучении это вызов основного потока join(), который препятствует обработке сигнала: сам Гвидо ван Россум объяснил, что получение базового замка в соединении является непрерывным . Это означает, что сигнал на самом деле откладывается до тех пор, пока не закончится весь поток - так что это вообще не имеет никакого отношения к raw_input (только тот факт, что фоновый поток блокируется, так что соединение не завершается).

Ответы [ 2 ]

6 голосов
/ 20 марта 2012

Когда соединение вызывается без тайм-аута, оно является непрерывным, но когда оно вызывается с тайм-аутом, оно прерываетсяПопробуйте добавить произвольное время ожидания и поместить его в цикл while:

while my_thread.isAlive():
    my_thread.join(5.0)
1 голос
/ 14 февраля 2012

Этого, на самом деле, просто не существует, точка.

Один из подходов состоит в том, чтобы реорганизовать и разбить ваш код таким образом, чтобы части функций, которым требуется прерываемая Ctrl-C выполнено в основном потоке.Вы используете очереди для отправки запросов на выполнение, а также для значений результатов.Вам нужна одна входная очередь для основного потока и одна выходная очередь для неосновного потока;и скоординированный выход из основного потока.Очевидно, что в данный момент времени выполняется только одна блокирующая функция, что может быть не тем, что вы хотите.

Вот рабочий пример этой идеи со слегка извращенным использованием семафоров дляскоординированный выход из основного потока.

...