As it often happens, the answer to your question about threads and Twisted is "don't use threads."
The reason you start the stream is because you can check the GPIO sensor multiple times. Does the sensor unit check? I guess not, because if it's a GPIO, it's locally available hardware and its results will be available right away. But I will give you the answer in both directions.
The main thing that you use for this topic is to do something repeatedly . If you want to repeat something in Twisted, there is never a reason to use streams :). Twisted includes an excellent API for repetitive tasks: LoopingCall . Your example rewritten to use LoopingCall (again, assuming the GPIO call is not blocking) would look like this:
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()
Of course, there is one case where you still need to use threads: if you need to perform a thread to check the GPIO (or any other your repetitive task), because it is a potentially blocking operation in the library that cannot be modified to make better use of Twisted, and you don’t want to block the main loop.
In this case, you still want to use LoopingCall and take advantage of one of its features: if you return Deferred from the function called by LoopingCall , it will not call it again until Deferred lights up. This means that you can transfer the task to the stream and not worry about the main loop filling in requests for this stream: you can simply resume the cycle in the main stream automatically when the stream ends.
To give you a more concrete idea of what I mean, here is the check_movement function, modified to work with a long-term blocking call that is executed in a thread, instead of a quick poll call that can be run on the main loop:
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)
Everything else in the above example remains the same.