简体   繁体   English

事件处理的扭曲模式

[英]Twisted Patterns for Event Handling

(Some assistance in forming this question in a better form is welcome as well) (也欢迎以更好的形式提出这个问题的一些帮助)

I'm looking at twisted for implementing a piece of asynchronous software that has absolutely nothing to do with the internet. 我正在考虑实施一个与互联网完全无关的异步软件。 I've also obtained a copy of Twisted Network Programming Essentials which was rather disappointing. 我还获得了Twisted Network Programming Essentials的副本,这是相当令人失望的。 It, and pretty much every other tutorial out there, seems to treat twisted as a networking client only, using built-in event handlers and silently using built in glue-code which is difficult to generalize. 它,以及几乎所有其他教程,似乎只是使用内置的事件处理程序并使用内置的胶水代码来处理扭曲的网络客户端,这很难概括。

For context, my application would want to talk to hardware devices using serial / pyserial and provide a gui using Qt. 对于上下文,我的应用程序希望使用serial / pyserial与硬件设备通信,并使用Qt提供gui。 I'm not even close to the point where I would start worrying about the Qt reactor (which looks to be yet another promise of a can of worms) or porting to windows. 我甚至没有接近我开始担心Qt反应堆(它看起来是一堆蠕虫的另一个承诺)或移植到Windows的地步。

For a start, I'm using a twisted select reactor, to which I've added a Protocol + FileDescriptor that handles udev events. 首先,我使用了一个扭曲的选择反应器,我已经添加了一个处理udev事件的Protocol + FileDescriptor。 What I have so far works, in that udev events trigger a function in the protocol (eventReceived). 到目前为止我的工作原理是udev事件触发协议中的一个函数(eventReceived)。 The following is the protocol and the way it's added to the reactor : 以下是协议及其添加到反应器的方式:

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()

The function init() is called by a reactor.callWhenRunning() just before reactor.run() . 该功能init()是由一种叫做reactor.callWhenRunning()之前reactor.run() The eventReceived function is called as expected by the FileDescriptor. fileDescriptor按预期调用eventReceived函数。 I can add that code here as well if it helps. 我可以在这里添加该代码,如果它有帮助。

What I would like is for eventRecieved to trigger some sort of event in the reactor which something else can react to. 我想要的是eventRecieved在反应堆中触发某种事件,而其他事情可以做出反应。 This code shouldn't care who's consuming this, and that code shouldn't care who's generating it. 这段代码不应该关心谁在使用它,而且代码不应该关心谁在生成它。 These events would be far and few between, and I can't seem to find an interface that'll be able to do this cleanly. 这些事件之间的距离很远,我似乎无法找到一个能够干净利落的界面。 The events are expected t come relatively infrequently, but they will never 'end'. 事件预计相对不频繁,但它们永远不会“结束”。 If a deferred is to be used, it would have to have some way of 'refreshing' itself to wait for the next event. 如果要使用延迟,则必须有某种方式“刷新”自己等待下一个事件。 What are the usual patterns for handling events such as this? 处理此类事件的常用模式是什么?

Edit: 编辑:

For the sake of posterity and anyone else looking, the rest of the code : 为了后代和其他任何人看,剩下的代码:

(from https://gist.github.com/dpnova/a7830b34e7c465baace7 ) (来自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")

Or the full module including connected_devices and searching for devices is up at https://gist.github.com/chintal/2511459c02a9767deb5d 或者包括connected_devices和搜索设备的完整模块在https://gist.github.com/chintal/2511459c02a9767deb5d

What I would like is for eventRecieved to trigger some sort of event in the reactor which something else can react to. 我想要的是eventRecieved在反应堆中触发某种事件,而其他事情可以做出反应。 This code shouldn't care who's consuming this, and that code shouldn't care who's generating it. 这段代码不应该关心谁在使用它,而且代码不应该关心谁在生成它。

The pattern for doing this that's widely used both in Twisted and by Twisted-using applications is “make a function call” (perhaps a method call). 在Twisted和Twisted-using应用程序中广泛使用的这种模式是“进行函数调用”(可能是方法调用)。

The reactor itself does not make a good single-process message bus. 反应堆本身并不是一个好的单进程消息总线。 There's been little motivation to try to make it into one because function calls by themselves work pretty well in the general case and when you get to the less common, specialized cases where they don't it's actually really hard to make something good (and once you do, you've still only serviced a tiny fraction of the possible audience of Twisted). 尝试将其合二为一的动机很少,因为函数调用本身在一般情况下工作得很好,当你遇到不那么常见的特殊情况时,他们不这样做,实际上很难做出好的东西(和你这样做,你仍然只为Twisted的一小部分观众提供服务。

Your code already includes an example of the kind of thing I'm talking about. 你的代码已经包含了我正在谈论的那种事情的例子。 UdevMonitor calls UdevMonitorListener.eventReceived when (I'm guessing) some kind of event comes out of the udev system. UdevMonitor调用UdevMonitorListener.eventReceived (我猜)某些事件来自udev系统。

Just repeat this. 请重复一遍。 Give your UdevMonitorListener a reference to another object and call some method on that object at the appropriate time. 为您的UdevMonitorListener提供对另一个对象的引用,并在适当的时间调用该对象上的某个方法。 For example: 例如:

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(...)

Also note I changed the base class to object . 另请注意,我将基类更改为object Protocol is a somewhat handy base class for IProtocol implementations - but that's not what UdevMonitorListener is, so Protocol isn't a good choice of base class. ProtocolIProtocol实现的一个有点方便的基类 - 但这不是UdevMonitorListenerUdevMonitorListener的,所以Protocol不是基类的好选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM