5

私はGnuradio フレームワークを使用しています。信号を送受信するために生成したフローグラフを処理します。これらのフローグラフは初期化して開始しますが、アプリケーションに制御フローを返しません。

輸入しましたtime

while time.time() < endtime:
        # invoke GRC flowgraph for 1st sequence
        if not seq1_sent:
            tb = send_seq_2.top_block()
            tb.Run(True)
            seq1_sent = True
            if time.time() < endtime:
                break

        # invoke GRC flowgraph for 2nd sequence
        if not seq2_sent:
            tb = send_seq_2.top_block()
            tb.Run(True)
            seq2_sent = True
            if time.time() < endtime:
                break

問題は、最初の if ステートメントだけがフローグラフ (ハードウェアと対話する) を呼び出すことです。私はこれで立ち往生しています。スレッドを使用できますが、Python でスレッドをタイムアウトにする方法は経験がありません。スレッドの強制終了は API 内にないように見えるため、これが可能であるとは思えません。このスクリプトは Linux でのみ動作する必要があります...

プログラム全体を強制終了することなく、Python でブロッキング関数を適切に処理するにはどうすればよいですか。この問題の別のより具体的な例は次のとおりです。

import signal, os

def handler(signum, frame):
        # print 'Signal handler called with signal', signum
        #raise IOError("Couldn't open device!")
        import time
        print "wait"
        time.sleep(3)


def foo():
    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(3)

    # This open() may hang indefinitely
    fd = os.open('/dev/ttys0', os.O_RDWR)
    signal.alarm(0)          # Disable the alarm


foo()
print "hallo"

どうすればまだ取得できますかprint "hallo"。;)

ありがとう、マリウス

4

8 に答える 8

6

まず第一に - シグナルの使用は絶対に避けるべきです:

1) デッドロックにつながる可能性があります。SIGALRM は、ブロッキング syscall の前にプロセスに到達する可能性があり (システム内の超高負荷を想像してください!)、syscall は中断されません。デッドロック。

2) シグナルをいじると、局所的でない厄介な結果が生じる可能性があります。たとえば、他のスレッドの syscall が中断される可能性がありますが、これは通常は望ましくありません。通常、syscall は (致命的ではない) シグナルが受信されると再開されます。シグナルハンドラーを設定すると、プロセス全体、つまりスレッドグループのこの動作が自動的にオフになります。その上で「man siginterrupt」を確認してください。

私を信じてください-私は以前に2つの問題に遭遇しましたが、それらはまったく楽しくありません.

場合によっては、ブロックを明示的に回避できます。書き込みと読み取りのブロックを処理するには、select() とその仲間 (Python の select モジュールを確認してください) を使用することを強くお勧めします。ただし、これは open() 呼び出しのブロックを解決しません。

そのために、このソリューションをテストしましたが、名前付きパイプでうまく機能します。ブロックしない方法で開き、それをオフにし、select() 呼び出しを使用して、何も利用できない場合は最終的にタイムアウトします。

import sys, os, select, fcntl

f = os.open(sys.argv[1], os.O_RDONLY | os.O_NONBLOCK)

flags = fcntl.fcntl(f, fcntl.F_GETFL, 0)
fcntl.fcntl(f, fcntl.F_SETFL, flags & ~os.O_NONBLOCK)

r, w, e = select.select([f], [], [], 2.0)

if r == [f]:
    print 'ready'
    print os.read(f, 100)
else:
    print 'unready'

os.close(f)

これを次の方法でテストします。

mkfifo /tmp/fifo
python <code_above.py> /tmp/fifo (1st terminal)
echo abcd > /tmp/fifo (2nd terminal)

さらに努力すれば、select() 呼び出しをプログラム全体のメイン ループとして使用して、すべてのイベントを集約することができます。libev や libevent、またはそれらを囲む Python ラッパーを使用できます。

非ブロッキング動作を明示的に強制できない場合、たとえば外部ライブラリを使用するだけの場合、それははるかに困難になります。スレッドはそうかもしれませんが、明らかにそれは最先端のソリューションではなく、通常は単に間違っています。

残念ながら、一般的にこれを堅牢な方法で解決することはできません。それは、何をブロックするかによって異なります。

于 2010-11-01T14:11:10.783 に答える
4

IIUC、各 top_block には stop メソッドがあります。したがって、実際にスレッドで top_block を実行し、タイムアウトになった場合は停止を発行できます。top_block の wait() にもタイムアウトがあればもっと良いのですが、残念ながらそうではありません。

次に、メイン スレッドで、a) top_block の完了、および b) タイムアウトの 2 つのケースを待機する必要があります。ビジー待機は悪です:-)、スレッドのタイムアウト付き結合を使用してスレッドを待機する必要があります。結合後もスレッドがまだ生きている場合は、top_run を停止する必要があります。

于 2010-10-29T13:04:31.407 に答える
2

タイムアウトで通話を中断する信号アラームを設定できます。

http://docs.python.org/library/signal.html

signal.alarm(1) # 1 second

my_blocking_call()
signal.alarm(0)

アプリケーションが破壊されないようにする場合は、シグナルハンドラーを設定することもできます。

def my_handler(signum, frame):
    pass

signal.signal(signal.SIGALRM, my_handler)

編集: このコードの何が問題になっていますか?これにより、アプリケーションが中止されることはありません。

import signal, time

def handler(signum, frame):
    print "Timed-out"

def foo():
    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(3)

    # This open() may hang indefinitely
    time.sleep(5)
    signal.alarm(0)          # Disable the alarm


foo()
print "hallo"

事は:

  1. SIGALRMのデフォルトのハンドラーは、アプリケーションを中止することです。ハンドラーを設定すると、アプリケーションが停止することはなくなります。

  2. シグナルを受信すると、通常、システムコールが中断されます(その後、アプリケーションのブロックが解除されます)

于 2010-10-29T12:51:24.603 に答える
2

あなたの質問の簡単な部分は、シグナル処理に関連しています。Python ランタイムの観点からは、インタープリターがシステム コールを行っている間に受信したシグナルは、errno対応する属性を持つ OSError 例外として Python コードに提示されます。errno.EINTR

したがって、これはおそらく意図したとおりに機能します。

    #!/usr/bin/env python
    import signal, os, errno, time

    def handler(signum, frame):
            # print 'Signal handler called with signal', signum
            #raise IOError("Couldn't open device!")
            print "timed out"
            time.sleep(3)


    def foo():
        # Set the signal handler and a 5-second alarm
        signal.signal(signal.SIGALRM, handler)

        try:
            signal.alarm(3)
            # This open() may hang indefinitely
            fd = os.open('/dev/ttys0', os.O_RDWR)
        except OSError, e:
            if e.errno != errno.EINTR:
                raise e
        signal.alarm(0)          # Disable the alarm

    foo()
    print "hallo"

timeそのようにインポートを非表示にするのは不適切な形式であるように思われるため、関数定義からインポートを移動したことに注意してください。なぜあなたがシグナルハンドラーで眠っているのか、私にはまったく明らかではありません。実際、それはかなり悪い考えのようです。

私がしようとしている重要なポイントは、(無視されていない) シグナルは、Python コード実行のメインラインを中断するということです。ハンドラーは、どのシグナル番号が実行をトリガーしたかを示す引数 (1 つの Python 関数をさまざまなシグナルの処理に使用できるようにする) とフレーム オブジェクト (デバッグや何らかの計測に使用できます) を使用して呼び出されます。

コードのメイン フローが中断されるため、そのようなイベントが発生した後に制御を取り戻すために、何らかの例外処理でそのコードをラップする必要があります。(ちなみに、C でコードを書いている場合は、同じ懸念があります。基礎となるシステム コールを持つライブラリ関数のいずれかがエラーを返し、システムerrnoで -EINTR を処理するために、再試行または再試行するためにループバックすることによって準備する必要があります。メインラインの代替への分岐(他のファイルに進む、またはファイル/入力なしなど)。

他の人があなたの質問への回答で示したように、SIGALARM に基づくアプローチは、移植性と信頼性の問題をはらんでいる可能性があります。さらに悪いことに、これらの問題のいくつかは、テスト環境では遭遇することのない競合状態である可能性があり、再現が非常に困難な条件下でのみ発生する可能性があります. 醜い詳細は再入可能性の場合にある傾向があります --- シグナルハンドラの実行中にシグナルがディスパッチされるとどうなりますか?

私はいくつかのスクリプトで SIGALARM を使用しましたが、Linux では問題になりませんでした。私が取り組んでいたコードは、タスクに適していました。あなたのニーズには十分かもしれません。

この Gnuradio コードがどのように動作するか、そこからインスタンス化するオブジェクトの種類、およびそれらが返すオブジェクトの種類について詳しく知らなければ、あなたの主な質問に答えることは困難です。

リンク先のドキュメントを一瞥すると、ブロッキング動作を直接制限するために使用できる「タイムアウト」引数や設定が提供されていないようです。.run()「フロー グラフの制御」の下の表では、無期限に、または SIGINT を受信するまで実行できると具体的に述べていることがわかります。また、アプリケーションでスレッドを開始できることにも注意して.start()ください。それらの実行中に、制御を Python コード行に戻すようです。(それはフローグラフの性質に依存しているようですが、私は十分に理解していません)。

フロー グラフを作成.start()し、(Python コードのメイン ラインでしばらく処理またはスリープした後).lock()制御オブジェクトのメソッドを呼び出すことができるように思えます (tb?)。これは、状態の Python 表現 ... Python オブジェクト ... を静止モードにして、状態を照会したり、フローグラフを再構成したりできるようにするものだと思います。呼び出す.run()と、呼び出し.wait()後に呼び出されます.start().wait()すべてのブロックが「完了したことを示す」か、オブジェクトの.stop()メソッドを呼び出すまで実行されるようです。

したがって、使用したいように聞こえますが.start()、どちら.run()もありません.wait(); .stop()その後、他の処理 ( を含む) を実行した後に呼び出しますtime.sleep()

おそらく次のような単純なものです。

    tb = send_seq_2.top_block()
    tb.start()
    time.sleep(endtime - time.time())
    tb.stop()
    seq1_sent = True
    tb = send_seq_2.top_block()
    tb.start()
    seq2_sent = True

.. 私は私のtime.sleep()そこを疑っていますが。おそらく、オブジェクトの状態を照会する場所で何か他のことをしたいでしょうtb(おそらく、より短い間隔でスリープし、その.lock()メソッドを呼び出し、私が何も知らない属性にアクセスし、.unlock()再びスリープする前にそれを呼び出します.

于 2010-11-06T14:27:59.300 に答える
1
if not seq1_sent:
        tb = send_seq_2.top_block()
        tb.Run(True)
        seq1_sent = True
        if time.time() < endtime:
            break

'if time.time()<endtime:'の場合、ループから抜け出し、seq2_sentのものがヒットすることはありません。おそらく、そのテストでは' time.time()> endtime'を意味しますか?

于 2010-11-04T22:05:26.047 に答える
0

遅延実行を使用してみることができます...ツイストフレームワークはそれらをたくさん使用します

http://www6.uniovi.es/python/pycon/papers/deferex/

于 2010-11-03T06:20:54.517 に答える
0

Pythonでスレッドを強制終了することについて言及しています-これは部分的に可能ですが、CコードではなくPythonコードが実行されている場合にのみ別のスレッドを強制終了/中断できるため、必要に応じて役に立たない場合があります。

別の質問に対するこの回答を参照してください: python: マルチスレッドでパケットを送信してから、スレッド自体を強制終了する方法

または、次のような詳細については、キル可能な python スレッドをグーグルで検索して ください。

于 2010-11-03T21:27:04.233 に答える
-1

ブロッキング関数にタイムアウトを設定したい場合は、threading.Thread をメソッド join(timeout) として使用し、タイムアウトまでブロックします。

基本的に、そのようなものはあなたが望むことをするはずです:

import threading
my_thread = threading.Thread(target=send_seq_2.top_block)
my_thread.start()
my_thread.join(TIMEOUT)
于 2010-10-29T12:37:04.117 に答える