在一个单独的线程中无限期地为所有连接生成内容?

时间:2012-01-17 17:30:29

标签: python twisted

我有一个Twisted项目,它试图在JSON中通过TCP重新广播收集的数据。我本质上有一个USB库,我需要订阅并在while循环中同步读取,如下所示:

while True:
    for line in usbDevice.streamData():
        data = MyBrandSpankingNewUSBDeviceData(line)
        # parse the data, convert to JSON
        output = convertDataToJSON(data)
        # broadcast the data
        ...

问题当然是...。基本上,我需要在服务器启动后立即启动此过程,并在服务器结束时(Protocol.doStartProtocol.doStop)结束它并使其持续运行并向每个连接的广播output运输。

我怎样才能在Twisted中执行此操作?显然,我需要在自己的线程中运行while循环,但我怎么能订阅"客户听取输出?同样重要的是,USB数据收集只运行一次,因为它可能会严重混乱,使其不止一次运行。

简而言之,这就是我的架构:

  1. 服务器有一个USB集线器,它始终是流式数据服务器不断订阅此USB集线器,并不断读取数据。
  2. 客户来去,连接和断开连接。
  3. 我们希望在可用时将数据发送到所有连接的客户端。我怎么能在Twisted中做到这一点?

1 个答案:

答案 0 :(得分:2)

您可能想要做的一件事是尝试扩展通用协议/传输独立性。即使您需要一个具有长时间运行循环的线程,您也可以将其隐藏在协议中。好处和往常一样:协议变得更容易测试,如果你设法有一个读取USB事件的非线程实现,你可以在不改变协议的情况下改变传输。

from threading import Thread

class USBThingy(Thread):
    def __init__(self, reactor, device, protocol):
        self._reactor = reactor
        self._device = device
        self._protocol = protocol

    def run(self):
        while True:
            for line in self._device.streamData():
                self._reactor.callFromThread(self._protocol.usbStreamLineReceived, line)

使用callFromThread是使该解决方案可用的部分原因。它确保在反应器线程中调用usbStreamLineReceived方法,而不是在从USB设备读取的线程中调用。因此,从协议对象的角度来看,线程没有什么特别之处:它只是在需要处理一些数据的时候暂时调用它的方法。

您的协议只需要以某种方式实现usbStreamLineReceived,并实现您的其他特定于应用程序的逻辑,例如保留观察者列表:

class SomeUSBProtocol(object):
    def __init__(self):
        self.observers = []

    def usbStreamLineReceived(self, line):
        data = MyBrandSpankingNewUSBDeviceData(line)
        # broadcast the data
        for obs in self.observers[:]:
            obs(output)

然后观察者可以用这个类的实例注册自己,并用数据做任何他们想做的事情:

class USBObserverThing(Protocol):
    def connectionMade(self):
        self.factory.usbProto.observers.append(self.emit)

    def connectionLost(self):
        self.factory.usbProto.observers.remove(self.emit)

    def emit(self, output):
        # parse the data, convert to JSON
        output = convertDataToJSON(data)
        self.transport.write(output)

把它们连在一起:

usbDevice = ...
usbProto = SomeUSBProtocol()
thingy = USBThingy(reactor, usbDevice, usbProto)
thingy.start()

factory = ServerFactory()
factory.protocol = USBObserverThing
factory.usbProto = usbProto
reactor.listenTCP(12345, factory)
reactor.run()

你可以想象一个更好的观察者注册/取消注册API(比如使用实际方法而不是直接访问该列表)。您还可以设想为USBThingy提供关闭方法,以便SomeUSBProtocol可以控制何时停止运行(因此您的流程实际上可以退出)。