< 返回版块

cococc 发表于 2022-10-09 09:49

以vnpy中的事件引擎为例,可以很方便的通过注册func到engine来方便的执行func所关注的事件数据,

而rust是对应的hashmap由于k/v需要强制统一类型,是不是就行不通了呢?

"""
Event-driven framework of vn.py framework.
"""

from collections import defaultdict
from queue import Empty, Queue
from threading import Thread
from time import sleep
from typing import Any, Callable, List

EVENT_TIMER = "eTimer"


class Event:
    """
    Event object consists of a type string which is used
    by event engine for distributing event, and a data
    object which contains the real data.
    """

    def __init__(self, type: str, data: Any = None):
        """"""
        self.type: str = type
        self.data: Any = data


# Defines handler function to be used in event engine.
HandlerType = Callable[[Event], None]


class EventEngine:
    """
    Event engine distributes event object based on its type
    to those handlers registered.

    It also generates timer event by every interval seconds,
    which can be used for timing purpose.
    """

    def __init__(self, interval: int = 1):
        """
        Timer event is generated every 1 second by default, if
        interval not specified.
        """
        self._interval: int = interval
        self._queue: Queue = Queue()
        self._active: bool = False
        self._thread: Thread = Thread(target=self._run)
        self._timer: Thread = Thread(target=self._run_timer)
        self._handlers: defaultdict = defaultdict(list)
        self._general_handlers: List = []

    def _run(self) -> None:
        """
        Get event from queue and then process it.
        """
        while self._active:
            try:
                event = self._queue.get(block=True, timeout=1)
                self._process(event)
            except Empty:
                pass

    def _process(self, event: Event) -> None:
        """
        First ditribute event to those handlers registered listening
        to this type.

        Then distrubute event to those general handlers which listens
        to all types.
        """
        if event.type in self._handlers:
            [handler(event) for handler in self._handlers[event.type]]

        if self._general_handlers:
            [handler(event) for handler in self._general_handlers]

    def _run_timer(self) -> None:
        """
        Sleep by interval second(s) and then generate a timer event.
        """
        while self._active:
            sleep(self._interval)
            event = Event(EVENT_TIMER)
            self.put(event)

    def start(self) -> None:
        """
        Start event engine to process events and generate timer events.
        """
        self._active = True
        self._thread.start()
        self._timer.start()

    def stop(self) -> None:
        """
        Stop event engine.
        """
        self._active = False
        self._timer.join()
        self._thread.join()

    def put(self, event: Event) -> None:
        """
        Put an event object into event queue.
        """
        self._queue.put(event)

    def register(self, type: str, handler: HandlerType) -> None:
        """
        Register a new handler function for a specific event type. Every
        function can only be registered once for each event type.
        """
        handler_list = self._handlers[type]
        if handler not in handler_list:
            handler_list.append(handler)

    def unregister(self, type: str, handler: HandlerType) -> None:
        """
        Unregister an existing handler function from event engine.
        """
        handler_list = self._handlers[type]

        if handler in handler_list:
            handler_list.remove(handler)

        if not handler_list:
            self._handlers.pop(type)

    def register_general(self, handler: HandlerType) -> None:
        """
        Register a new handler function for all event types. Every
        function can only be registered once for each event type.
        """
        if handler not in self._general_handlers:
            self._general_handlers.append(handler)

    def unregister_general(self, handler: HandlerType) -> None:
        """
        Unregister an existing general handler function.
        """
        if handler in self._general_handlers:
            self._general_handlers.remove(handler)

评论区

写评论
作者 cococc 2022-10-20 17:10

谢谢。最开始入门学习的时候有看过。又看了几遍,感觉没有太多相同点呢。

--
👇
DogLi: 如果非要这么干的话,拿走不谢: https://doc.rust-lang.org/book/ch17-02-trait-objects.html

DogLi 2022-10-09 16:40

如果非要这么干的话,拿走不谢: https://doc.rust-lang.org/book/ch17-02-trait-objects.html

作者 cococc 2022-10-09 15:52

感谢您的回复。 在vnpy的框架中,Event的data属性可以是任意类别的对象,比如可以是dict,可以是实例化的class、str等等。 对这类,您所说的设计成静态泛型,如何去理解呢

PS:按照给的提示,已clone tokio-rs/axum 在学习。

--
👇
songzhi: Event里面带的数据应该可以设计成静态泛型,不用dyn Any,可以参考一下axum是怎么实现的。

--
👇
songzhi: 这里用enum和dyn trait的主要区别是用户可不可扩展,用了enum之后用户就没法增加新的事件类型了。你贴的Python代码转换成Rust相当于dyn trait+Any。

songzhi 2022-10-09 15:15

Event里面带的数据应该可以设计成静态泛型,不用dyn Any,可以参考一下axum是怎么实现的。

--
👇
songzhi: 这里用enum和dyn trait的主要区别是用户可不可扩展,用了enum之后用户就没法增加新的事件类型了。你贴的Python代码转换成Rust相当于dyn trait+Any。

songzhi 2022-10-09 15:06

这里用enum和dyn trait的主要区别是用户可不可扩展,用了enum之后用户就没法增加新的事件类型了。你贴的Python代码转换成Rust相当于dyn trait+Any。

作者 cococc 2022-10-09 14:24

冒昧问下,怎么个方便法呢?

--
👇
Mike Tang: 很方便,但是需要转换一下思路,毕竟从py过来的多少有些不适应。

作者 cococc 2022-10-09 14:24

第一种方式下,所有的事件构成一个枚举没问题,但处理的时候,是不是就需要match了?

所贴的代码,他可以很方便的在整个程序中传递这个实例,然后执行func的注册,这样func在任何地方都可以。

而以我浅薄的对rust认知而言,好像处理起来就需要match集中到一起挨个适配。

--
👇
shanliu: 有两个方法 第一写个EMUN,把可能的事件枚举进去 第二用dyn triat [不建议] 涉及生命周期等情况的时候会比较复杂

Mike Tang 2022-10-09 12:40

很方便,但是需要转换一下思路,毕竟从py过来的多少有些不适应。

github.com/shanliu/lsys 2022-10-09 10:01

有两个方法 第一写个EMUN,把可能的事件枚举进去 第二用dyn triat [不建议] 涉及生命周期等情况的时候会比较复杂

1 共 9 条评论, 1 页