0

私はpythonを使用して、ベンダーが提供するpython APIを使用してハードウェアusbスニファーデバイスをインターフェースしており、無限ループの別のスレッドでデバイスから(usbパケット)を読み取ろうとしています(これは正常に動作します)。問題は、私のメイン ループが再びスケジュールされないように見えることです (私の読み取りループがすべての注目を集めます)。

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

from threading import Thread
import time
usb_device = 0

def usb_dump(usb_device):
    while True:
        #time.sleep(0.001)
        packet = ReadUSBDevice(usb_device)
        print "packet pid: %s" % packet.pid

class DumpThread(Thread):
    def run(self):
        usb_dump()

usb_device = OpenUSBDevice()
t = DumpThread()
t.start()
print "Sleep 1"
time.sleep(1)
print "End"
CloseUSBDevice(usb_device)
sys.exit(0)

(実際のコードを貼り付けることもできますが、ハードウェア デバイスが必要なので、あまり役に立たないと思います)。

メインスレッドがプログラム全体を終了する前に、このコードが約 1 秒間 USB パケットのダンプを開始することを期待しています。ただし、表示されるのは「Sleep 1」だけで、usb_dump()手順は永久に実行されます。プロシージャの内部ループで「time.sleep(0.001)」ステートメントのコメントを外すと、usb_dump()期待どおりに動作し始めますが、Python コードは入ってくるすべてのパケットに追いつくことができなくなります :-(

ベンダーは、これは Python スケジューラの問題であり、API のせいではないため、役に立たないと言います。

«しかし、Python でスレッド化を使用する場合、いくつかのニュアンスが発生しているようです。DumpThread スレッドに time.sleep を配置することで、制御を放棄するように Python スレッド システムに明示的に信号を送ることになります。それ以外の場合、いつスレッドを切り替えるかを決定するのは Python インタープリターであり、通常は一定数のバイトコード命令が実行された後にそれを行います。»

ここでpythonが問題であることを誰かが確認できますか? DumpThread リリース コントロールを作成する別の方法はありますか? 他のアイデアはありますか?

4

3 に答える 3

3

あなたのベンダーが純粋なPythonコードであれば、あなたのベンダーは正しいでしょう。ただし、C拡張機能はGILを解放する可能性があるため、実際のマルチスレッド化が可能です。

特に、time.sleepGILを解放します(ソースコードから直接確認できます。ここで-floatsleep実装を見てください)。したがって、コードに問題はないはずです。さらなる証拠として、USBへの呼び出しを削除するだけの簡単なテストも行いましたが、実際には期待どおりに機能します。

from threading import Thread
import time
import sys

usb_device = 0

def usb_dump():
    for i in range(100):
        time.sleep(0.001)
        print "dumping usb"

class DumpThread(Thread):
    def run(self):
        usb_dump()

t = DumpThread()
t.start()
print "Sleep 1"
time.sleep(1)
print "End"
sys.exit(0)

最後に、投稿したコードに関するいくつかのメモ:

  • usb_deviceがスレッドに渡されていません。これをパラメーターとして渡すか、(argh!)グローバル名前空間から取得するようにスレッドに指示する必要があります。
  • sys.exit()を強制する代わりに、スレッドに停止するように通知してからUSBデバイスを閉じる方がよい場合があります。現在のように、コードでマルチスレッドの問題が発生する可能性があると思います。
  • 定期的なポーリングだけが必要な場合は、threading.Timerクラスの方が適している場合があります。

[更新]最新のポイントについて:コメントで述べられているTimerように、は関数のセマンティクス(定期的なポーリング)によりよく適合し、ベンダーコードによってリリースされないGILの問題を自動的に回避すると思います。

于 2009-07-30T09:35:48.313 に答える
2

ReadUSBDevice関数を公開するPython Cモジュールを作成し、USBパケットが受信されるまでブロックしてから返すことを意図していると仮定しています。

ネイティブの ReadUSBDevice 実装は、USB パケットを待機している間に Python GIL を解放し、受信時に再取得する必要があります。これにより、ネイティブ コードの実行中に他の Python スレッドを実行できます。

http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

GIL のロックを解除している間は、Python にアクセスできません。GIL を解放し、ブロッキング関数を実行してから、Python に戻す必要があることがわかったら、それを再取得します。

これを行わないと、ネイティブ ブロッキングの実行中に他の Python スレッドを実行できなくなります。これがベンダー提供の Python モジュールである場合、ネイティブ ブロッキング アクティビティ中に GIL を解放できないのはバグです。

多くのパケットを受信し、実際に Python でそれらを処理している場合、他のスレッドは引き続き実行する必要があることに注意してください。実際に Python コードを実行している複数のスレッドは並行して実行されませんが、スレッド間で頻繁に切り替えられるため、すべてのスレッドが実行される可能性があります。GIL を解放せずにネイティブ コードがブロックしている場合、これは機能しません。

編集:これはベンダー提供のライブラリであると言及しました。ソースがない場合、GIL が解放されているかどうかを確認する簡単な方法: USB アクティビティが発生していないときに ReadUSBDevice スレッドを開始すると、ReadUSBDevice はデータを待機するだけになります。彼らが GIL を解放している場合、他のスレッドは妨げられずに実行されるはずです。そうでない場合は、インタープリター全体がブロックされます。それは深刻なバグになります。

于 2009-07-30T09:15:37.650 に答える