首页 > 其他分享 >klippy — reactor模块

klippy — reactor模块

时间:2025-01-07 09:47:27浏览次数:1  
标签:协程 klippy self timer next dispatch 模块 ._ reactor

  • 该模块负责管理事件的注册、调度和处理,充当事件驱动的核心引擎,驱动整个klippy系统的运行。
  • 该模块提供了一个统一的接口register_callback,使各个模块能够注册自己的回调函数以响应特定的事件。
  • 使用事件循环的方式,不断地检查事件的状态并触发相应的回调函数。

reactor模式

Reactor 模式是一个事件驱动的编程模式,它允许程序以非阻塞的方式处理多个 I/O 操作。这个模式主要包含四个核心组件:

  1. 事件循环(Event Loop):它负责不断监听事件,并将其分发给相应的处理器。
  2. 反应堆(Reactor):作为事件循环的管理者,它监视一组资源,等待事件发生。
  3. 资源(Resources):通常是网络套接字或文件描述符,是反应堆监视的对象。
  4. 事件处理器(Event Handlers):每个事件都有相应的处理器来响应。

入口

try:
    select.poll
    Reactor = PollReactor
except:
    Reactor = SelectReactor
  • 如果支持 select.poll,则调用PollReactor,否则,调用SelectReactor

初始化

class PollReactor(SelectReactor):
    def __init__(self, gc_checking=False):
        SelectReactor.__init__(self, gc_checking)
        self._poll = select.poll()
        self._fds = {}
class SelectReactor:
    NOW = _NOW
    NEVER = _NEVER
    def __init__(self, gc_checking=False):
        # Main code
        self._process = False
        self.monotonic = chelper.get_ffi()[1].get_monotonic
        # Python garbage collection
        self._check_gc = gc_checking
        self._last_gc_times = [0., 0., 0.]
        # Timers
        self._timers = []
        self._next_timer = self.NEVER
        # Callbacks
        self._pipe_fds = None
        self._async_queue = queue.Queue()
        # File descriptors
        self._fds = []
        # Greenlets
        self._g_dispatch = None
        self._greenlets = []
        self._all_greenlets = []
  • PollReactor 继承自SelectReactor,在初始化的时候,调用SelectReactor初始化,对timers、文件描述符、Greenlets等进行初始化。
def run(self):
    if self._pipe_fds is None:
        self._setup_async_callbacks()
    self._process = True
    g_next = ReactorGreenlet(run=self._dispatch_loop)
    self._all_greenlets.append(g_next)
    g_next.switch()
  • run方法 在klippy.py 主循环 打印机对象初始化后,会调用该方法。
  • self._process: 设置True 为运行状态。
  • ReactorGreenlet(run=self._dispatch_loop):生成协程,进入事件主循环协程。
  • g_next.switch():切换到协程g_next。

事件驱动主循环

def _dispatch_loop(self):
    self._g_dispatch = g_dispatch = greenlet.getcurrent()
    busy = True
    eventtime = self.monotonic()
    while self._process:
        timeout = self._check_timers(eventtime, busy)
        busy = False
        res = self._poll.poll(int(math.ceil(timeout * 1000.)))
        eventtime = self.monotonic()
        for fd, event in res:
            busy = True
            self._fds[fd](eventtime)
            if g_dispatch is not self._g_dispatch:
                self._end_greenlet(g_dispatch)
                eventtime = self.monotonic()
                break
    self._g_dispatch = None

该方法实现了基于poll函数的事件循环,通过轮询文件描述符上的事件,并根据事件类型调用相应的回调函数进行处理。在处理完一个事件后,如果需要切换到其他协程进行执行,则使用greenlet实现切换。整个过程不断循环,直到_process 为False,则结束事件循环。

  • eventtime = self.monotonic():获取当前的时间
  • timeout = self._check_timers(eventtime, busy):检查是否有任何定时器事件需要处理。该方法返回下一个定时器事件的超时时间。
  • self._poll.poll(int(math.ceil(timeout * 1000.))):轮询操作,等待文件描述符上的事件发生。
  • self._fdsfd: 调用文件描述符相应的回调函数。主要监听本地socket文件和一个reactor的双向管道文件描述符。
  • if g_dispatch is not self._g_dispatch: 判断当前的greenlet与事件循环的greenlet是否相同,不同则切换协程。
  • self._end_greenlet(g_dispatch):结束当前协程,并切换到其他协程。

协程

  • greenlet 是python实现协程的一个三方库,是一种基于协作式的多任务编程模型,允许在同一线程内实现多个并发执行的任务。
  • greenlet 是协作和顺序的。当一个 greenlet 运行时,其他 greenlet 都不能运行;开发者可以完全控制何时在 greenlet 之间切换执行。
  • 协作式调度: 协程通常使用协作式调度,这意味着它们在适当的时机主动让出控制权,从而允许其他协程执行。这种方式可以有效避免线程切换的开销。
  • 事件循环: 在许多协程实现中(如 Python 的 asyncio),协程是在事件循环中运行的。事件循环负责调度和管理协程的执行。
  • I/O 密集型任务: 协程特别适合处理 I/O 密集型任务(如网络请求和文件操作),因为在等待 I/O 操作完成时,协程可以挂起自己,让其他协程继续执行。

定时器注册

def register_timer(self, callback, waketime=NEVER):

    timer_handler = ReactorTimer(callback, waketime)
    timers = list(self._timers)
    timers.append(timer_handler)
    self._timers = timers
    self._next_timer = min(self._next_timer, waketime)
    return timer_handler
  • 将一个回调函数注册到定时器队列中

定时器检查

def _check_timers(self, eventtime, busy):
    if eventtime < self._next_timer:
        if busy:
            return 0.
        if self._check_gc:
            gi = gc.get_count()
            if gi[0] >= 700:
                # Reactor looks idle and gc is due - run it
                gc_level = 0
                if gi[1] >= 10:
                    gc_level = 1
                    if gi[2] >= 10:
                        gc_level = 2
                self._last_gc_times[gc_level] = eventtime
                gc.collect(gc_level)
                return 0.
        return min(1., max(.001, self._next_timer - eventtime))
    self._next_timer = self.NEVER
    g_dispatch = self._g_dispatch
    for t in self._timers:
        waketime = t.waketime
        if eventtime >= waketime:
            t.waketime = self.NEVER
            t.waketime = waketime = t.callback(eventtime)
            if g_dispatch is not self._g_dispatch:
                self._next_timer = min(self._next_timer, waketime)
                self._end_greenlet(g_dispatch)
                return 0.
        self._next_timer = min(self._next_timer, waketime)
    return 0.
  • 该函数主要是通过检查定时器的到期时间,决定是否执行定时器的回调函数,以及更新下一次检查定时器的时间。
  • 如果未到定时器执行时间,判断状态是否忙碌,忙碌直接返回;否则进行垃圾回收操作
  • 遍历定时器列表,回调定时器函数进行处理
  • 在触发回调函数后,检查是否发生了协程切换。如果 g_dispatch 和当前的 _g_dispatch 不一致,意味着其他协程已经接管控制,结束当前协程并返回。
  • 循环结束后,更新下次要检查的定时器时间 self._next_timer,确保系统按时检查到期的定时器。

定时器更新

def update_timer(self, timer_handler, waketime):
    timer_handler.waketime = waketime
    self._next_timer = min(self._next_timer, waketime)
  • 主要是更新定时器的执行时间,是否立即执行。

标签:协程,klippy,self,timer,next,dispatch,模块,._,reactor
From: https://www.cnblogs.com/logicalsky/p/18656841

相关文章

  • IPoIB模块初始化:深入解析Linux内核模块的初始化过程
    在Linux内核中,模块初始化是确保模块能够正确加载并运行的关键步骤。IPoverInfiniBand(IPoIB)模块作为一种网络技术模块,允许通过InfiniBand网络高效传输IP数据包。本文将深入解析IPoIB模块的初始化函数,展示其如何通过一系列配置和注册步骤为模块的运行做好准备。IPoIB模块的......
  • LPDDRX功能模块详细介绍
    LPDDRX(LowPowerDoubleDataRateX)是一种专为低功耗应用设计的内存技术,广泛应用于手持设备、电池供电设备和超便携设备中。LPDDRX功能模块详细介绍如下:ControlLogic(控制逻辑):负责接收和处理来自内存控制器的命令,并根据这些命令协调其他功能模块的操作。控制逻辑是LPD......
  • FMC子卡设计原理图:165-2路万兆光纤SFP+ FMC子卡模块
    2路万兆光纤SFP+FMC子卡模块1.概述该板卡是基于kc705和ml605的fmc10g万兆光纤扩展板设计。SFP+(10GigabitSmallFormFactorPluggable)是一种可热插拔的,独立于通信协议的光学收发器,通常传输光的波长是850nm,1310nm或1550nm,用于10Gbps的SONE......
  • 光模块特性及眼图测试
    项目目标熟练掌握OptiSystem软件的使用方法。熟悉光模块的主要参数及其测试方法。理解光接收机灵敏度的概念及其影响因素。掌握眼图的形成原理,了解抖动的产生机制,并能够利用眼图评估数字传输系统的数据传输能力,同时学会通过眼图分析信号抖动。了解光发射机和光接收机性能......
  • KUKA库卡机器人驱动模块维修常见故障
    KUKA库卡机器人驱动模块是用来控制伺服电机的一种控制器,其作用类似于变频器作用于普通交流马达,属于驱动模块的一部分,主要应用于高精度的定位系统。一般是通过位置、速度和力矩三种方式对驱动马达进行控制,实现高精度的传动系统定位,目前是传动技术的产品。下面我们介绍下对KUKA库卡......
  • 在Lazarus下的Free Pascal编程教程——以数据处置推动程序运行的模块化程序设计方法
    0.前言我想通过编写一个完整的游戏程序方式引导读者体验程序设计的全过程。我将采用多种方式编写具有相同效果的应用程序,并通过不同方式形成的代码和实现方法的对比来理解程序开发更深层的知识。了解我编写教程的思路,请参阅体现我最初想法的那篇文章中的“1.编程计划”和“2.已......
  • 玩转物联网-4G模块如何快速将数据上传到百度云平台
    目录1前言2环境搭建2.1硬件准备2.2软件准备2.3硬件连接2.4检查驱动3百度智能云设备创建3.1账号创建3.2进入百度智能云物联网核心套件平台3.3创建实例3.4创建模板3.5添加设备3.6获取设备连接信息4连接百度智能云4.1打开配置工具读取基本信息4.......
  • 玩转物联网-4G模块如何快速将数据上传到OneNET平台(自动注册)
    目录1前言2环境搭建2.1硬件准备2.2软件准备2.3硬件连接2.4检查驱动3创建和获取OneNET平台连接信息3.1创建账号3.2创建产品3.3设置物模型3.4获取注册设备信息4连接OneNET4.1打开配置工具读取基本信息4.2设置模块连接参数并进行数据交互5.总结......
  • 一键隐藏和恢复VBA代码模块,适用于SolidWorks和Excel宏
    自从上次发表了微文《一种隐藏VBA代码模块以及恢复可见的方法》之后,有许多粉丝朋友纷纷表示这种方法很有用,可以更好地保护自己写的代码,并想要更深入探究和学习。经过小编这段时间的潜心研究,终于把这个方法写成了exe格式的可执行程序,下面来看看具体怎样对VBA代码模块进行隐藏和恢......
  • Python入门教程 —— 模块和包
    1.导入模块Python中的模块在Python中有一个概念叫做模块(module)。说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块。比如我们经常使用工具random,就是一个模块。使用importrandom导入工具之后,就可以使用random的函数。导入模......