2015-10-03 9 views
9

私は無限ループ上で動作し、データベースに物事を追加し、途中で止めることができないので、単にCtrl + Cキーを押して停止することはできません。Pythonで無限ループを安全に止めるには?

私は何とかwhileループを停止したいが、停止する前に最後のiterationを終了させたい。

私は明確にしましょう:私は最後にwhileループ、または先頭を中断することができるようにしたい

while True: 
    does something 
    does more things 
    does more things 

ではなくので、物事をやっ間:

私のコードは次のようになりますそれは悪いです。

と私はそれを繰り返したいと思っています。偉大な答えを

おかげで、私はスーパー感謝していますが、私の実装が動作していないようです:

def signal_handler(signal, frame): 
     global interrupted 
     interrupted = True 

class Crawler(): 
    def __init__(self): 
    # not relevent 

    def crawl(self): 
     interrupted = False 
     signal.signal(signal.SIGINT, signal_handler) 
     while True: 
      doing things 
      more things 

      if interrupted: 
       print("Exiting..") 
       break 

私はプログラムcはCTRの+を押したときにちょうど私

+0

あなたのスクリプトで、ジョブが完了した(最後のもの)かどうかを判断する方法はありますか?この場合、条件で使用してbreak文を使用すると、 – praba230890

+0

この場合、ジョブは無限に進むことができるWebクローラです。私はクロールを止めるように伝えることができますが、例えばページの途中で中断するだけではありません。 – davegri

+0

*停止する前に最後の反復を終わらせる*?それが仕事をしたかどうか、どうやって知っていますか? –

答えて

9

あなたがする必要がある何が、割り込みをキャッチし、あなたが中断されたと言ってフラグを設定したが、それはそれぞれの終わりに(フラグをチェックする時間になるまで作業を続けていますループ)。 Pythonのtry-except構造はループの現在の実行を中止するので、適切なシグナルハンドラを設定する必要があります。それは割り込みを処理しますが、Pythonを中断したところから続けるようにします。ここで方法は次のとおりです。

import signal 

import time # For the demo only 

def signal_handler(signal, frame): 
    global interrupted 
    interrupted = True 

signal.signal(signal.SIGINT, signal_handler) 


interrupted = False 
while True: 
    print("Working hard...") 
    time.sleep(3) 
    print("All done!") 

    if interrupted: 
     print("Gotta go") 
     break 

注:

  1. コマンドラインからこれを使用してください。 IDLEコンソールでは、IDLE自身の割り込み処理を踏みにじります。

  2. より良い解決策は、ループの継続中はKeyboardInterruptを「ブロック」し、割り込みをポーリングする時はブロック解除することです。これはいくつかのUnixフレーバーの機能ですが、すべてではありません。したがって、Python does not support it(第3の「一般規則」を参照してください)

  3. OPはこれをクラス内で実行します。しかし、割り込み関数はシグナルハンドリングシステムによって呼び出され、シグナル番号とスタックフレームへのポインタの2つの引数を持ちます。つまり、クラスオブジェクトにアクセスする引数はselfです。したがって、フラグを設定する最も簡単な方法は、グローバル変数を使用することです。クロージャを使用してローカルコンテキストへのポインタを設定することができます(つまり、__init__()でシグナルハンドラを動的に定義しますが、マルチスレッドなどの理由でグローバルが問題にならない限り、気にしません)。

警告:あなたのプロセスは、システムコールの途中である場合は、信号を処理することは、システムコールを中断することがあります。したがって、これはすべてのアプリケーションにとって安全ではない可能性があります。より安全な選択肢は、(a)信号に頼る代わりに、各ループ反復の最後に非ブロック読み取りを使用します(そして、^ Cを押すのではなく入力します)。 (b)信号処理から作業者を分離するためにスレッドまたはプロセス間通信を使用する。あなたがそれを持っているOSを使っているなら、(c)実signal blockingを実装する作業をしてください。それらはすべてOSに依存しているため、そのままにしておきます。

+0

私は理解していると思いますが、私の関数はクラスで実行されているので、signal_handlerをクラスの関数として定義する必要がありますか? – davegri

+0

シグナルハンドラは、通常の制御フローの外で呼び出されます。あなたはそれをクラスメソッドにする必要はありませんし、それがうまくいくかどうかは分かりません。 [signal](https://docs.python.org/3/library/signal.html)のドキュメントを参照してください... – alexis

+0

短い答え:トップレベルの関数とグローバル変数を使用してください。あなたがマルチスレッドなどでなければ、人生は難しくなります... – alexis

9

を無視していっています以下のロジックは

import signal 
import sys 
import time 

run = True 

def signal_handler(signal, frame): 
    global run 
    print "exiting" 
    run = False 

signal.signal(signal.SIGINT, signal_handler) 
while run: 
    print "hi" 
    time.sleep(1) 
    # do anything 
    print "bye" 

これを実行している間、

0
CTRL + Cを押してみてください、あなたがこれを行うのに役立ちます私はあなたを助けるでしょうコードの下に願っています

#!/bin/python 

import sys 
import time 
import signal 

def cb_sigint_handler(signum, stack): 
    global is_interrupted 
    print "SIGINT received" 
    is_interrupted = True 

if __name__ == "__main__": 
    is_interrupted = False 
    signal.signal(signal.SIGINT, cb_sigint_handler) 
    while(1): 
     # do stuff here 
     print "processing..." 
     time.sleep(3) 
     if is_interrupted: 
      print "Exiting.." 
      # do clean up 
      sys.exit(0) 
0

@ praba230890の解決策を明確にする:interrupted変数が正しい範囲で定義されていませんでした。 crawl関数で定義されており、ハンドラがプログラムのルートにあるハンドラの定義に従ってグローバル変数としてハンドラに到達できませんでした。

関連する問題