事件驱动模型
事件驱动模型使用三个队列来管理事件的运行。这三个队列是,基本事件队列、事件句柄队列和待执行句柄队列。
·基本事件队列:每个事件对应一个队列项,每个队列项包括一项事件句柄指针,指向该事件的事件句柄队列;一项事件种类,它是基本事件之一;一项本事件未处理的句柄总数,说明还有多少事件等待处理;一项已处理旬柄总数,说明已经发生的事件总数。
事件句柄队列:每类事件对应一个事件句柄队列。一个句柄队列项包括三部分,即程序句柄序列号、事件的程序句柄和事件句柄优先级。序列号区分不同通信时所对应的事件,事件句柄说明该事件发生时执行的程序首地址。
待执行句柄队列:存放将要执行的事件句柄。事件句柄队列的事件按优先级存储在待执行队列中。处在该队列中的事件由冲洗程序驱动执行,一次冲洗程序的调用将企图驱动该队列中的全部事件的执行。
事件队列初始化每当进行一次通信时,由方法驱动事件按照所需执行的事件种类,将事件对应的程序句柄及其优先级加入到事件程序句柄队列。
除了方法驱动事件之外,一次POST通信至少有如下8个事件发生:应用层方法发送事件(0)、消息层发送事件(10)、数据层发送事件(20)、传输层发送事件(28)、传输层接收事件(8)、数据层接收事件(10)、消息层接收事件(20)和应用层方法完成事件(30)。
事件驱动时机当一次通信的事件被初始化后,可以立即驱动事件执行,也可由事件冲洗功能在以后驱动执行。如果每次通信都立即驱动执行,那么事件驱动模型就与层次模型兼容。使用立即驱动方法不能改进通信性能。在网格系统中,主要采用冲洗功能执行事件句柄。采用冲洗功能,若干个通信可以流水地进行,有利于提高系统的通信效率。1
事件句柄当待执行句柄队列中有事件句柄时,使用事件句柄冲洗功能将队列中的句柄按优先级和连接标记顺序执行。连接标记用来说明本次程序完成之后是继续(Continue)执行后续句柄指定程序还是就此停止(Stop)。例如,在传输层发送事件之后,应该中止本次事件序列。当传输层接收到信号(中断)时,驱动接收事件序列,直到应用层的方法完成事件为止。HandlerFlush子程序的基本框架如下:
|| ||
事件冲洗当有事件在事件队列时,可以使用事件冲洗功能。事件冲洗功能对每个事件对应的句柄队列进行冲洗,每次将具有相同序号的句柄冲洗到等待执行的句柄队列,并由事件冲洗函数EventFlush(event)冲洗执行队列,如此重复。EventFlush(event)函数的基本框架如下:
|| ||
Verilog的层次化事件队列详细地了解Verilog的层次化事件队列有助于如何理解Verilog的阻塞和非阻塞赋值的功能。所谓层次化事件队列指的是用于调度仿真事件的不同的Verilog事件队列。在IEEEVerilog标准中,层次化事件队列被看作是一个概念模型。设计仿真工具的厂商如何来实现事件队列,由于关系到仿真器的效率,被视为技术诀窍,不能公开发表,本节也不作详细介绍。
在IEEEl364—1995Verilog标准的5.3节中定义了:层次化事件队列在逻辑上分为用于当前仿真时间的4个不同的队列,和用于下一段仿真时间的若干个附加队列。
动态事件队列动态事件队列(下列事件执行的顺序可以随意安排):
阻塞赋值;
计算非阻塞赋值语句右边的表达式;
连续赋值;
执行$display命令;
计算原语的输入和输出的变化。
停止运行的事件队列停止运行的事件队列:
#0延时阻塞赋值。
非阻塞事件队列非阻塞事件队列:
更新非阻塞赋值语句LHS(左边变量)的值。
监控事件队列监控事件队列:
执行$monitor命令;
执行$strobe命令。
其他指定的PLI命令队列其他指定的PLI命令队列
以上5个队列就是Verilog的“层次化事件队列”。
动态事件队列大多数Verilog事件是由动态事件队列调度的,这些事件包括阻塞赋值、连续赋值、$dis—play命令、实例和原语的输入变化以及它们的输出更新、非阻塞赋值语句RHS的计算等。而非阻塞赋值语句LHS的更新却不由动态事件队列调度。2
在IEEE标准允许的范围内被加入到这些队列中的事件只能从动态事件队列中清除,其他队列中的事件要等到被“激活”后,即被排人动态事件队列中后,才能真正开始等待执行。IEEEl364—1995Verilog标准的5.4节介绍了一个描述其他事件队列何时被“激活”的算法。
非阻塞赋值更新事件队列和监控事件队列在当前仿真时间中,另外两个比较常用的队列是非阻塞赋值更新事件队列和监控事件队列。非阻塞赋值LHS变量的更新是按排在非阻塞赋值更新事件队列中,而RHS表达式的计算是在某个仿真时刻随机地开始的,与上述其他动态事件是一样的。
$strobe和$monitor显示命令是排列在监控事件队列中。在仿真的每一步结束时刻,当该仿真步骤内所有的赋值都完成以后,$strobe和$monitor显示出所有要求显示的变量值的变化。
在Verilog标准5.3节中描述的第4个事件队列是停止运行事件队列,所有井0延时赋值都排列在该队列中。采用#0延时赋值是因为对Verilog理解不够深入的设计人员希望在两个不同的程序块中给同一个变量赋值,他们企图在同一个仿真时刻,通过稍加延时赋值来消除Verilog可能产生的竞争冒险。这样做实际上会产生问题。因为给Verilog模型附加完全不必要的#o延时赋值,使得定时事件的分析变得很复杂。我们认为采用#0延时赋值根本没有必要,完全可用其他的方式来代替,因此不推荐使用。
RunLoop的事件队列简介每次运行RunLoop, 线程中的RunLoop会自动处理线程中的任务, 并且通知观察者, 汇报当前的状态, 顺序如下
通知观察者RunLoop已经启动
通知观察者任何即将要开启的定时器
通知观察者任何即将要启动的非基于端口的事件源
启动任何准备好的非基于端口的事件源
如果基于端口的事件源准备好并处于等待状态, 就立即启动, 并且进入步骤9
通知观察者线程即将进入休眠
将线程置于休眠状态, 直至以下事件的发生
某一事件到达基于端口的源事件
定时器启动
RunLoop设置的事件已经超时
RunLoop被显式唤醒
通知观察者线程即将被唤醒
处理事件
如果用户定义的定时器启动, 处理定时器事件并且重启RunLoop, 然后进入步骤23
如果输入源启动, 传递响应的信息
如果RunLoop被现实唤醒, 并且事件还没超时, 重启RunLoop, 进入步骤2
通知观察者RunLoop结束
RunLoop的一般应用NSTimer和GCD定时器
PerformSelector: afterDelay
当调用这个方法的时候, 实际内部会创建一个Timer并且添加到当前的RunLoop中, 如果当前线程没有RunLoop, 这个方法也就会失效
在子线程中开启一个RunLoop, 做为常驻线程
自动释放池
手势识别等等