2012-02-13 3 views
5

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

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

import threading 

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

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

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

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

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

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


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

+3

Не просто комбинировать потоки и перерывы ... [boromir.jpg] – JBernardo

ответ

0

В этом периоде нет простого способа передвижения.

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

Here's a working example of this idea с немного извращенным использованием семафоров для согласованного выхода основной нити.

3

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

while my_thread.isAlive(): 
    my_thread.join(5.0) 
Смежные вопросы