AsyncIO Micropython:异步编程中的同步方法

在顺序编程中,我不断遇到明显的愿望,即在某些任务(过程)的目标是周期性动作时(例如轮询传感器值或按时间表将数据传输到服务器或输入/输出大量数据)不停止程序。 当然,最简单的事情是等待周期事件的完成,然后慢慢地继续执行其他任务。

while True: do_ext_proc_before() do_internal_proc() sleep(5) do_ext_proc_after() 

您可以放弃'sleep()'并启用对循环中某些条件的检查,这将使您至少在发生周期性事件之前不要延迟主循环:

  start = time() set_timer(start,wait=5) #   set_timeout(start,wait_to=7) #   set_irq(alarm) #    while True: curTime = time() do_ext_proc_before() if timer(curTime) or timeout(curTime) or alarm: # if all events crazy start simultaneously - reset all start = time() set_timer(start,wait=5) #   set_timeout(start,wait_to=7) #   set_irq(alarm) #    do_internal_proc() do_ext_proc_after() 

在异步编程中,每个任务都变成一个独立的过程,并根据特定的实现方式,通过对自然或人为建立的等待条件的内部了解或使用有限的资源(例如磁盘或通信通道)来并行或伪并行地执行。

  setTask(do_ext_proc_before()) setTask(do_internal_proc(),timer=5,timeout=7,alarm_handler=alarm) setTask(do_ext_proc_after()) runTasks() 

现在出现了顺序编程中不存在的问题-如果有必要将某些进程与其异步同步,该怎么办
在干嘛 例如,已从传感器接收数据,启动将数据发送到服务器或响应紧急情况的过程。 此外,在异步编程中,异步输入/输出的组织在语言标准中得到有机解决,而其他情况则在库中得到解决。

我使用发布的asyncio Micropython扩展库研究了这个问题
彼得·辛奇(Peter Hinch)( https://github.com/peterhinch/micropython-async/blob/master/TUTORIAL.md
最简单的解决方案是将事件通知感兴趣的进程。 为此,请使用Event()类,其中包含几个模块

  Event.Set( timeout = None, data = None ) -    (Event = True), ,     , Event.IsSet() - ,   ,  True,    False   Event.Wait() -   ,     - Done,Timeout,Cancel Event.Data() -  ,     Event.Clear() -   (Event = False). 

通常,通过等待事件发生的过程(例如,在屏幕上显示的过程或将数据保存到磁盘的过程或超时)来记录完成情况,则由于任何原因均未更新数据,因此无需更新或保存数据,或者由于它在发生另一个重要事件(例如,转换为睡眠模式或重新启动)时中断,因此可能需要通过重置相应事件来释放所有挂起的进程。

应该记住的是,如果不与给定算法矛盾,建议仅使用一个进程来制作Event.Clear()。 否则,如果几个进程正在等待事件Event.Set()发生,则假定Event.Clear()应该由感兴趣的进程之一执行,仅确保所有感兴趣的进程均已响应该事件。 当等待多个进程的事件时,使用事件类时,这会使决策逻辑变得复杂。 通过为发生的事件建立一定数量的Clear()解决此情况。

  Barrier.Set( quantity = 1, timeout = None, data = None ) - quantity = 1  Event.Set() Barrier.IsSet() - ,   ,  True,    False   Barrier.Wait() -   ,     - Done,Timeout,Cancel Barrier.Data() -  ,     Barrier.qty -      Barrier.Clear() -   (Event = False),        Barrier.quantity  ,    ,     

同时,不保留任何记帐-哪个特定的过程已经响应,哪个尚未响应,如果这对于给定算法是必不可少的,则可能引起对该事件重复反应的问题。 如果通过传递感兴趣的进程的名称列表而不是Barrier.quantity,则可以避免这种冲突。 此外,如果事件超时或中断,您可以确定哪些特定的挂起流程尚未运行。 以上所有情况适用于一个或多个进程正在等待某个事件发生的情况,或一对多的情况。 当仅在do_internal_proc()完成之后才执行顺序编程期间的一个或多个进程do_ext_proc_after()时,会发生这种情况。 为了方便进一步理解,我们将现有的Event-Class和Barrier-Class扩展为新的EEvent-Class,并使它或由它生成的对象成为全局对象。 这里的“创建者”是触发事件或解锁资源的进程的名称或名称列表,“跟随者”是等待事件或解锁资源的进程的名称或名称列表

  EEvent.Set (creators, folowers, timeout = None, data = None ) -  True,        EEvent.IsSet( procName ) - procName -   ID   EEvent.Wait( procName ) EEvent.Clear( procName ) EEvent.Folowers() -    ,      . Barrier.qty = len(EEvent.List()) EEvent.Creators() -   ,     

使用EEvent-Class模块,我们可以描述前面讨论的问题的解决方案。

  def do_internal_proc(): ... EEvent.Set ('p_Creator',('p_Folwer1','p_Folwer2')) # exec 'p_Folwer1','p_Folwer2' after event is come in 'p_Creator' ... def do_ext_proc_after1() ... EEvent.Wait('p_Creator') ... EEvent.Clear('p_Folwer1') def do_ext_proc_after1() ... EEvent.Wait('p_Creator') ... EEvent.Clear('p_Folwer2') 

考虑相反的情况-当一个进程正在等待多个事件的完成时,或者是“一对多”的情况。 换句话说,如果do_internal_proc()的执行只能在do_ext_proc_before()的执行之后执行,在极端情况下,当一个进程在等待一个事件的完成/发生时,可以使用Event-class解决该任务。 例如,当预期完成几个事件时,仅在显示接收到的数据并将其发送到服务器并将其保存到磁盘后,才需要每个执行的进程确定其参与预期的事件并等待所有进程完成。

  def do_ext_proc_before1() ... EEvent.Set('p_Creator1','p_Folwer') ... def do_ext_proc_before2() ... EEvent.Set('p_Creator2','p_Folwer') ... def do_internal_proc(): ... EEvent.Wait(('p_Creator1','p_Creator2')) ... EEvent.Clear('p_Folwer') 

异步编程的另一个重要方面是共享有限的资源。 例如,数据更新仅应由一个进程执行,其他声称类似操作的进程必须排队或等待直到数据被更新。 同时,读取用于显示或转发的数据可能并不重要。 因此,在组织相关事件时有必要了解竞争过程的列表。

在异步编程标准中,此任务由Lock-Class模块解决。 在一般情况下,也可以类似于一对多的情况来解决问题。

  def do_internal_proc(): # lock activity all 'followers' in list ... EEvent.Set ('p_Creator',('p_Folwer1','p_Folwer2')) # exec 'p_Folwer1','p_Folwer2' after event is come in 'p_Creator' ... def do_ext_proc_after1() ... EEvent.Wait('p_Creator') # waiting for recourse releale if ( EEvent.Set ('p_Folwer1','p_Folwer2')): # lock resource 'p_Folower1' now is 'p_Creator' ... else: EEvent.Wait('p_Folower2') # continue waiting for recourse releale ... EEvent.Clear('p_Folwer1') # releafe recourse def do_ext_proc_after1() ... EEvent.Wait('p_Creator') if ( EEvent.Set ('p_Folwer2','p_Folwer1')): # lock resource 'p_Folower2' now is 'p_Creator' ... else: EEvent.Wait('p_Folower1') # continue waiting for recourse releale ... EEvent.Clear('p_Folwer2') # releafe recourse 

除了所考虑的选项之外,还有一些解决方案可以限制吞吐量,组织队列和控制进程的调度,但是在我的活动中仍然不需要这样做,因此也需要对我自己有足够的了解,尽管我不排除存在更优雅的做法或经济决策。

总之,我要说的是,顺序和异步方法具有同等的权利,可以成功实现给定算法。 因此,此方法或方法的应用由创建者的优先级决定-在实现给定算法时,这对创建者而言更为重要-透明度和可读性,生成代码的速度或数量。

Source: https://habr.com/ru/post/zh-CN442268/


All Articles