web-dev-qa-db-ja.com

Pythonで無限ループを安全に停止する方法は?

無限ループで実行され、データベースに何かを追加し、途中で停止できないことを行うスクリプトがあるので、Ctrl + Cを押して停止することはできません。

どういうわけか、whileループを停止できるようにしたいのですが、停止する前に最後の反復を終了させます。

明確にさせてください:

私のコードは次のようになります:

while True:
    does something
    does more things
    does more things

私は、whileループを最後または最初で中断できるようにしたいのですが、それが悪いため、物事の間に中断することはできません。

そして、私が継続したいかどうか、毎回の繰り返しの後に尋ねられたくありません。

素晴らしい答えをありがとう、私はとても感謝していますが、私の実装は機能していないようです:

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

ctr + cを押すと、プログラムは私を無視し続けます

13
davegri

あなたがする必要があるのは、割り込みをキャッチし、割り込みを受けたことを示すフラグを設定しますが、(各ループの終わりに)フラグをチェックする時まで作業を続けます。 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 サポートしていません (3番目の「一般規則」を参照)

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

警告:プロセスがシステムコールの途中にある場合、シグナルを処理するとシステムコールが中断される場合があります。したがって、これはすべてのアプリケーションに対して安全であるとは限りません。より安全な代替策は、(a)信号に依存する代わりに、各ループ反復の終わりにノンブロッキング読み取りを使用します(^ Cを押す代わりに入力を入力します)。 (b)スレッドまたはプロセス間通信を使用して、ワーカーを信号処理から分離します。または(c)実装しているOSを使用している場合は、実際の シグナルブロッキング を実装します。それらはすべてある程度OSに依存しているので、それはそのままにしておきます。

17
alexis

以下のロジックはこれを行うのに役立ちます、

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"

これを実行中に、CTRL + Cを押してみてください

10
praba230890

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

0
Anthony Scemama

以下のコードがあなたに役立つことを願っています:

#!/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
pdave