7

この説明とこの回避策を参照します。

だから私がやっていることは次のとおりです。

def interrupted(signum, stackframe):
    log.warning('interrupted > Got signal: %s', signum)
    menu.quitMenu = True # to stop my code

signal.signal(signal.SIGINT, interrupted) # Handle KeyboardInterrupt

問題は、メニューが停止する必要があることを通知され、すぐに停止することですraw_input

def askUser(self):
    current_date   = datetime.now().isoformat(' ')
    choice = raw_input('%s > ' % current_date)
    return choice

したがって、twisted はデフォルトの割り込みハンドラを削除しているため、raw_input停止しません。enter停止するには、後で押す必要があり^Cます。

デフォルトの割り込みハンドラーをインストールせずに raw_input を強制的に停止するにはどうすればよいですか。これはねじれたコンテキストで問題の原因となります (ねじれたコンテキスト自体は中断されることを予期していないため)。

raw_inputこの問題は、無制限の時間 (または事前設定された制限よりも長い時間) を要する関数は、何らかの方法で中断する必要があるということだけに関連しているとは思いません。

これに対して受け入れられているねじれたパターンはありますか?

編集

これは完全なテスト コードです。

from datetime import datetime

class Menu:

    def __init__(self):
        self.quitMenu = False

    def showMenu(self):
        print '''

A) Do A
B) Do B

'''

    def askUser(self):
        current_date   = datetime.now().isoformat(' ')
        choice = raw_input('%s > Please select option > ' % current_date)
        print
        return choice

    def stopMe(self):
        self.quitMenu = True

    def alive(self):
        return self.quitMenu == False

    def doMenuOnce(self):
        self.showMenu()
        choice = self.askUser()
        if not self.alive() : # Maybe somebody has tried to stop the menu while in askUser
            return
        if   choice == 'A' : print 'A selected'
        elif choice == 'B' : print 'B selected'
        else               : print 'ERR: choice %s not supported' % (choice)

    def forever(self):
        while self.alive():
            self.doMenuOnce()

from twisted.internet import reactor, threads
import signal

class MenuTwisted:

    def __init__(self, menu):
        self.menu = menu
        signal.signal(signal.SIGINT, self.interrupted) # Handle KeyboardInterrupt

    def interrupted(self, signum, stackframe):
        print 'Interrupted!'
        self.menu.stopMe()

    def doMenuOnce(self):
        threads.deferToThread(self.menu.doMenuOnce).addCallback(self.forever)

    def forever(self, res=None):
        if self.menu.alive() :
            reactor.callLater(0, self.doMenuOnce)
        else : 
            reactor.callFromThread(reactor.stop)

    def run(self):
        self.forever()
        reactor.run()

2つの異なる方法で実行できます。

通常の方法:

menu = Menu()
menu.forever()

を押すと^C、プログラムがすぐに停止します。

A) Do A
B) Do B


2013-12-03 11:00:26.288846 > Please select option > ^CTraceback (most recent call last):
  File "twisted_keyboard_interrupt.py", line 72, in <module>
    menu.forever()
  File "twisted_keyboard_interrupt.py", line 43, in forever
    self.doMenuOnce()
  File "twisted_keyboard_interrupt.py", line 34, in doMenuOnce
    choice = self.askUser()
  File "twisted_keyboard_interrupt.py", line 22, in askUser
    choice = raw_input('%s > Please select option > ' % current_date)
KeyboardInterrupt

予想通り。

ねじれた方法:

menu = Menu()
menutw = MenuTwisted(menu)
menutw.run()

押すと、次のもの^Cが生成されます。

A) Do A
B) Do B


2013-12-03 11:04:18.678219 > Please select option > ^CInterrupted!

しかしaskUser、実際には中断されていません。終了enterするには、まだ押す必要があります。raw_input

4

1 に答える 1

3

これに対処する正しい方法は、ブロッキング入力関数を割り込み可能にしようとするのではなく、コンソール入力を非同期で処理することです。言い換えれば、raw_input攻撃している問題に対する根本的な間違った解決策です。

ただし、ここで何が起こっているのかを本当に理解したい場合は、 を呼び出した後、何らかの方法で終了reactor.callFromThread(reactor.stop)するように促す必要があります。raw_input普通にはいきません。ただし、スレッドで実行しているため、実際にはまったく割り込み可能ではありません。Python ではメイン スレッドのみが割り込み可能であるためです。ですから、あなたが望むことは実際には不可能かもしれないと思います。sys.stdinスレッド内であっても、閉じると敷物が下から引き出される可能性があると私は信じていraw_inputましたが、基盤となるライブラリは単に FD から読み取るよりも賢いことをしているように見えるので、閉じることは役に立ちません。

于 2013-12-04T12:13:51.373 に答える