事件驱动编程中的执行上下文

时间:2016-11-19 09:47:55

标签: events context-switch

我正在阅读本书中关于事件驱动编程的内容:

Practical UML Statecharts in C/C++, 2nd Edition: Event-Driven Programming for Embedded Systems

在页面上。 xxv​​iii简介,作者说:

  

......事件驱动的应用程序必须在处理后返回控制   每个事件,所以执行上下文不能保留在   基于堆栈的变量和程序计数器,因为它是顺序的   程序。相反,事件驱动的应用程序成为一种状态   机器,或实际上是一组协作状态机   保留静态中从一个事件到下一个事件的上下文   变量

我无法理解为什么在处理事件后返回控件后,执行上下文无法在基于堆栈的变量和程序计数器中保留?

1 个答案:

答案 0 :(得分:1)

让我们从传统顺序编程范例的工作原理开始。假设您想要使嵌入式电路板上的LED闪烁。一个常见的解决方案是编写这样的程序(例如,参见Arduino Blink tutorial):

while (1) { /* RTOS task or a "superloop" */
    turn_LED_on();  /* turn the LED on  (computation) */
    delay(500);     /* wait for 500 ms (polling or blocking) */
    turn_LED_off(); /* turn the LED off (computation) */
    delay(1000);    /* wait for 1000 ms (polling or blocking) */
}

这里的关键点是delay()函数,它会一直等待直到延迟过去。这种等待被称为“阻塞”,因为调用程序被阻塞,直到delay()返回。

请注意,Blinky程序会在两个不同的上下文中调用delay()turn_LED_on()之后的第一次和turn_LED_off()之后的第二次。每次delay()返回代码中的其他位置。这意味着当程序被阻止时,代码中的位置信息(调用的上下文)将自动保留。

琐碎的Blinky程序非常简单,但原则上可以从其他函数调用阻塞函数,如delay(), 复杂的if-else-while代码。仍然,delay()将能够返回到调用的确切位置,因为C编程语言保留了调用的上下文(在调用堆栈和程序计数器中)。

但阻止会使整个程序对任何其他事件都没有反应,因此人们想出了event-driven programming

事件驱动程序围绕event-loop构建。示例事件驱动代码可能如下所示:

while (1) { /* event-loop */
    Event *e = queue_get(); /* block when event queue is empty */
    dispatch(e); /* handle the event, cannot block! */
}

重点是dispatch()“事件处理程序”函数无法调用阻塞函数,如delay()。相反,dispatch()只能执行一些立即操作,必须快速返回回到事件循环。这样,事件循环始终保持响应

但是,通过返回dispatch()函数从调用堆栈中删除自己的堆栈帧。因此,与调用dispatch()相关联的调用堆栈和程序计数器始终是相同的,并且无法“记住”执行上下文。

相反,要使LED闪烁,dispatch()功能必须依赖于记住LED状态(开/关)的某个变量(state)。您可以编写此类dispatch()函数的示例如下:

static enum {OFF, ON } state = OFF; /* start in the OFF state */
timer_arm(1000); /* arm a timer to generate TIMEOUT event in 1000 ms */

void dispatch(Event *e) {
    switch (state) {
        case OFF:
            if (e->sig == TIMEOUT) {
                turn_LED_on();
                timer_arm(500);
                state = ON; /* transition to "ON" state */
            }
        break;
        case ON:
            if (e->sig == TIMEOUT) {
                turn_LED_off();
                timer_arm(1000);
                state = OFF; /* transition to "OFF" state */
            }
        break;
    }
}

我希望你能看到dispatch()通过一个事件TIMEOUT驱动状态ON和OFF来实现state machine