4

Raspberry Pi の GPIO を使用して動きを感知し、サイレンを作動させるホーム セキュリティ プログラムを Python で作成しました。ユーザーは、ラズベリー パイにも接続されている nfc リーダーに NFC タグを使用して、システムをアクティブ化/非アクティブ化します。

このために、ブロックしない方法で nfc タグを常にチェックすると同時に、センサーの動きもブロックしないように常にチェックする必要があります。もう少し並行して行う必要がありますが、この 2 つで十分だと思います。

現在、このように開始/停止するスレッドを使用しています-一定時間後にスレッドを停止する-これが最適な方法であるかどうかはわかりませんが、現在、システムは正常に動作しています。

ここで、WebSocket を介して通知を提供するように機能を拡張したいと考えています。これは Twisted で実行できることがわかりましたが、混乱しています..

これが私がそれをやろうとしている方法のサンプルコードです:

from twisted.internet import reactor
from autobahn.websocket import WebSocketServerFactory, \
                               WebSocketServerProtocol, \
                               listenWS


def thread1(stop_event):
    while(not stop_event.is_set()):
        stop_event.wait(4)
        print "checking sensor"
        # sensor_state = GPIO.input(11)
        if sensor_state == 1:
            # how can I call send_m("sensor detected movement")  #<---
            t1_stop_event.set()

t1_stop_event = Event()
t1 = Thread(target=thread1, args=(t1_stop_event,))

class EchoServerProtocol(WebSocketServerProtocol):
   def onMessage(self, msg, binary):
    print "received: "+msg
    print "stopping thread1"
    t1_stop_event.set()

   def send_m(self, msg):
    self.sendMessage(msg)

if __name__ == '__main__':
   t1.start()
   factory = WebSocketServerFactory("ws://localhost:9000")
   factory.protocol = EchoServerProtocol
   listenWS(factory)
   reactor.run()

では、thread1 のようなスレッドからサーバー プロトコルの send メソッドを呼び出すにはどうすればよいでしょうか。

4

2 に答える 2

5

よくあることですが、スレッドと Twisted に関するあなたの質問に対する答えは、「スレッドを使用しないでください」です。

ここでスレッドを開始する理由は、GPIO センサーを繰り返しチェックできるようにするためです。センサーブロックをチェックしていますか?GPIO の場合はローカルで利用可能なハードウェアであり、その結果はすぐに利用できるため、そうではないと思います。しかし、私はあなたに両方の方法で答えます。

ここでスレッドを使用する主な目的は、何かを繰り返し実行することです。Twisted で何かを繰り返し実行したい場合、スレッドを使用する理由はありません:)。Twisted には、反復タスク用の優れた API が含まれています: LoopingCall. 使用するために書き直された例LoopingCall(GPIO 呼び出しがブロックされないことを前提としています) は、次のようになります。

from somewhere import GPIO

from twisted.internet import reactor, task
from autobahn.websocket import WebSocketServerFactory, \
                               WebSocketServerProtocol, \
                               listenWS

class EchoServerProtocol(WebSocketServerProtocol):

    def check_movement(self):
        print "checking sensor"
        sensor_state = GPIO.input(11)
        if sensor_state == 1:
            self.send_m("sensor detected movement")

    def connectionMade(self):
        WebSocketServerProtocol.connectionMade(self)
        self.movement_checker = task.LoopingCall(self.check_movement)
        self.movement_checker.start(4)

    def onMessage(self, msg, binary):
        self.movement_checker.stop()

    def send_m(self, msg):
        self.sendMessage(msg)

if __name__ == '__main__':
   factory = WebSocketServerFactory("ws://localhost:9000")
   factory.protocol = EchoServerProtocol
   listenWS(factory)
   reactor.run()

もちろん、まだスレッドを使用する必要がある場合が 1 つあります。GPIO チェッカー (または定期的なタスクが何であれ) をスレッドで実行する必要がある場合です。これは、変更できないライブラリでの操作をブロックする可能性があるためです。 Twisted をより有効に活用し、メイン ループをブロックしたくありません。

その場合でも、 を使用しLoopingCall、その機能の別の 1 つを利用したいと考えています。呼び出しDeferredている関数からa を返すと、 が起動LoopingCallするまでその関数は再度呼び出されませんDeferred。これは、タスクをスレッドに移動できることを意味し、メイン ループがそのスレッドのクエリを積み上げることを心配する必要はありません。スレッドが完了すると、メイン スレッドでループを自動的に再開できます。

私が言いたいことをより具体的に理解check_movementするために、メイン ループで実行できるクイック ポーリング呼び出しの代わりに、スレッドで実行される長時間実行ブロッキング呼び出しで動作するように変更された関数を次に示します。

def check_movement(self):
    from twisted.internet.threads import deferToThread
    def get_input():
        # this is run in a thread
        return GPIO.input(11)
    def check_input(sensor_state):
        # this is back on the main thread, and can safely call send_m
        if sensor_state == 1:
            self.send_m("sensor movement detected")
    return deferToThread(get_input).addCallback(check_input)

上記の例に関する他のすべては、まったく同じままです。

于 2013-07-22T21:30:13.833 に答える
2

あなたの例にはいくつかの要因があります。短い答え: Twisted のスレッドに関するこのドキュメントを調べてください。

  • プロトコル クラスを使用するために Twisted のリアクターを使用する必要はありませんreactor.run(スレッド化とプロトコルの実装は分離されています)、呼び出したので、以下のすべてが適用されると考えられます。
  • Twisted にスレッドを作成させてください。枠組みの外に出ると、問題が発生する可能性があります。リアクターとの IPC メッセージング用の「パブリック」API はありません (私はそう思います)。
  • デフォルトでは、Twisted はコールバックを呼び出すためにスレッドを切り替えません。メインのリアクター スレッドからワーカー スレッドに委任する (つまり、ブロッキング I/O を実行する) には、自分でスレッドを作成する必要はありません。スレッドを使用するreactor.callInThreadと、ワーカー スレッドで実行されます。これを行わないと、すべてがメインのリアクター スレッドで実行されます。つまり、たとえば、I/O 操作によってリアクター スレッドがブロックされ、I/O が完了するまでイベントを受け取ることができなくなります。
  • ワーカー スレッドで実行されるコードreactor.callFromThreadは、スレッド セーフではないことを行うために を使用する必要があります。メインのリアクタ スレッドで実行されるコールバックを提供します。ここで後悔するよりは安全です、私を信じてください。
  • Deferred上記のすべては、処理にも適用されます。したがって、コールバックを設定するときは、単にpartial(reactor.callFromThread, mycallback)orpartial(reactor.callInThread, mycallback)の代わりに使用することを恐れないでください。mycallback私はそれを難し​​い方法で学びました。それがなければ、遅延コールバックで実行する可能性のあるブロッキング I/O は、(スレッド セーフの問題により) エラーが発生するか、メイン スレッドをブロックするかのいずれかであることがわかりました。

Twisted を使い始めたばかりの場合、それはちょっとした "信頼の低下" です。Queue独自のスレッドの管理や、オブジェクトを介したメッセージの受け渡しなどを手放すことを学びましょう。Deferredリアクターがどのように機能するか (理由から「ツイスト」と呼ばれています) を理解すれば、それは完全に自然なことのように思えるでしょう。Twisted では、関数型プログラミング スタイルで懸念事項を切り離して分離することが強制されますが、それが終わると、非常にクリーンでうまく機能することがわかりました。

1 つのヒント: すべてのコールバック関数で使用するデコレーターをいくつか作成したので、コード全体で常に例外処理コールバックを呼び出しcallInThreadcallFromThreadセットアップする必要がなくなりました。Deferred私のデコレータは私のためにその動作を可能にします。バグがそれを忘れるのを防いでくれたようで、間違いなく Twisted の開発が私にとってより楽しいものになりました。

于 2013-07-20T04:26:33.313 に答える