事件处理的扭曲模式

时间:2015-03-19 20:20:29

标签: python twisted

(也欢迎以更好的形式提出这个问题的一些帮助)

我正在考虑实施与互联网完全无关的异步软件。我还获得了Twisted Network Programming Essentials的副本,这是相当令人失望的。它,以及几乎所有其他教程,似乎只是将扭曲视为一个网络客户端,使用内置的事件处理程序,并默默使用内置的胶水代码,这很难概括。

对于上下文,我的应用程序希望使用serial / pyserial与硬件设备通信,并使用Qt提供gui。我甚至没有接近我开始担心Qt反应堆(它看起来是一堆蠕虫的另一个承诺)或移植到Windows的地步。

首先,我使用了一个扭曲的选择反应器,我已经添加了一个处理udev事件的Protocol + FileDescriptor。到目前为止我的工作原理是udev事件触发协议中的一个函数(eventReceived)。 以下是协议及其添加到反应堆的方式:

class UdevMonitorListener(Protocol):
    def __init__(self, _reactor=None):
        if _reactor is not None:
            self._reactor = _reactor
        else:
            self._reactor = reactor
        self.subsystem = 'tty'
        self.monitor = None

    def startListening(self):
        logger.info("Starting UdevMonitorListener")

        self.monitor = UdevMonitor(self._reactor, self, self.subsystem)
        self.monitor.startReading()

    def eventReceived(self, action, device):
        if device in connected_devices.udev_ports:
            if action == u'remove':
                connected_devices.remove_by_udev_port(device)
        if action == u'add':
            if is_device_supported_from_udev_port(device):
                if device not in connected_devices.udev_ports:
                    connected_devices.append_by_udev_port(device)


def init(_reactor=None):
    monitor_protocol = UdevMonitorListener(_reactor)
    monitor_protocol.startListening()

init()之前reactor.callWhenRunning()调用函数reactor.run()。 fileDescriptor按预期调用eventReceived函数。如果它有帮助,我可以在这里添加该代码。

我想要的是eventRecieved在反应堆中触发某种事件,而其他事情可以做出反应。这段代码不应该关心谁在消费它,而且代码不应该关心谁生成它。这些事件之间的距离很远,我似乎无法找到一个能够干净利落地完成这项工作的界面。事件预计相对不频繁,但它们永远不会结束#39;如果要使用延迟,则必须采用某种方式来刷新'本身等待下一个事件。处理此类事件的常用模式是什么?

编辑:

为了后人和其他任何人看,其余的代码:

(来自https://gist.github.com/dpnova/a7830b34e7c465baace7

class UdevMonitor(FileDescriptor):
    """
    File Descriptor for pyudev.Monitor.
    @see: U{http://packages.python.org/pyudev/api/monitor.html}.
    """
    def __init__(self, _reactor, protocol, subsystem=None):
        FileDescriptor.__init__(self, _reactor)

        # Set up monitor
        context = pyudev.Context()
        self.monitor = pyudev.Monitor.from_netlink(context)
        if subsystem:
            self.monitor.filter_by(subsystem=subsystem)

        # Connect protocol
        assert isinstance(protocol, UdevMonitorListener)
        self.protocol = protocol
        self.protocol.makeConnection(self)

    def fileno(self):
        """
        Return monitor's file descriptor.
        """
        return self.monitor.fileno()

    def startReading(self):
        """
        Start waiting for read availability.
        """
        logger.debug("starting udev monitor fd")
        self.monitor.start()
        FileDescriptor.startReading(self)

    def doRead(self):
        """
        An event is ready, decode it through Monitor and call our protocol.
        """
        logger.debug("udev reports event available")
        event = self.monitor.receive_device()
        if event:
            action, device = event
            self.protocol.eventReceived(action, device)

    def writeSomeData(self, data):
        raise IOError("You can't write to a udev Monitor")

或者包括connected_devices和搜索设备的完整模块在https://gist.github.com/chintal/2511459c02a9767deb5d

处启动

1 个答案:

答案 0 :(得分:4)

  

我想要的是eventRecieved在反应堆中触发某种事件,而其他事情可以做出反应。这段代码不应该关心谁在消费它,并且代码不应该关心谁生成它。

在Twisted和Twisted-using应用程序中广泛使用的这种模式是“进行函数调用”(可能是方法调用)。

反应堆本身并不是一个好的单进程消息总线。尝试将其合二为一的动机很小,因为在一般情况下,函数调用本身工作得非常好,当你遇到不常见的专业案例时,他们不会这样做。实际上很难做出好的事情(而且一旦你这样做,你仍然只为Twisted的一小部分观众提供服务)。

您的代码已经包含了我正在谈论的那种事情的示例。当我(我猜测)某些事件来自udev系统时,UdevMonitor会调用UdevMonitorListener.eventReceived

重复一遍。为UdevMonitorListener提供对另一个对象的引用,并在适当的时间调用该对象上的某个方法。例如:

class UdevMonitorListener(object):
    def __init__(self, device_listener, _reactor=None):
        ...
        self.device_listener = device_listener

    def eventReceived(self, ...)
        ...
        self.device_listener.some_device_thing_happened(...)

另请注意,我将基类更改为object。对于Protocol实现,IProtocol是一个有点方便的基类 - 但这不是UdevMonitorListener的基础,所以Protocol不是基类的好选择