处理事件
正如在基于事件的编程中已经介绍的,select语句允许代码等待事件发生。它可以在不同的情况下等待不同的事件:
select {
case A:
...
break;
case B:
...
break;
case C:
...
break;
}
本节将描述这种事件处理语言结构的一些高级功能。
守护(Guards)
当select在等待多个事件时,有些事件是条件性启用的。即,只有当某个守护表达式评估为非零时,代码才应该对它们作出反应。其语法如下:
case expr => ... :
以下示例只在变量periodic_enabled非零时响应计时器事件:
int periodic_enabled ;
timer tmr ;
uint32_t t;
...
select {
case periodic_enabled => tmr when timerafter (t) :> void :
..
break;
case ...
}
如果被保护的案例是一个接口交易,那么编译器需要额外的信息来实现保护。为了使编译器能做到这一点,如果程序中的任何地方都对接口函数进行了保护,那么必须在接口声明中使用[[guarded]]属性标记为可能被保护。例如:
interface if1 {
void f();
[[guarded]] void g(); // 这个函数可能在程序中被保护
}
..
select {
case i.f() :
...
break;
case e => i.g():
...
break;
}
顺序
通常,在select语句中的事件没有优先级。如果在执行select时有多个事件准备就绪,选择的事件是未指定的。
有时,通过使用[[ordered]]属性可以强制设置优先级,表示select中的事件按照从高到低的优先级排序。例如,如果在allowing示例中的所有三个事件在select执行时都准备就绪,将选择case A:
[[ordered]]
select {
case A:
...
break;
case B:
...
break;
case C:
...
break;
}
有序的select不能在可组合或可分布式函数中使用。
默认情况
通常,select会等待直到其中一个事件发生。可以在select中添加一个默认case。如果在第一次执行select时没有其他事件准备就绪,将触发默认case:
select {
case A:
...
break;
case B:
...
break;
default:
...
break;
}
默认情况不能在可组合或可分布式函数中使用。
复制case
复制的case会多次迭代相同的case。如果程序有一个要对其做出反应的资源数组,这很有用。例如,下面的代码对一个计时器数组和相关超时进行迭代。
timer tmr_array[5];
uint32_t timeout[5];
unit32_t period[5];
...
select {
case (size_t i = 0; i < 5; i++)
tmr_array[i] when timerafter(timeout[i]) :> void:
...
timeout[i] += period[i];
break;
}
select函数
您可以在一个选择性函数中集合多个select case。这些函数能够让您抽象出部分select以供重用,进而创建包含反应性行为的函数库。
以下示例定义了一个具有两个case的选择函数:
#include <print.h>
#include <xs1.h>
timer t;
select my_case(chanend c, unsigned timeout) {
case c :> int x:
printintln(x);
break;
case t when timerafter(timeout) :> void:
printstrln("Timeout");
break;
}
请注意,函数定义之前有select关键字。选择函数的主体只能包含一个case列表。
可以按照以下方式在select内调用选择函数:
void f(chanend c, chanend d, chanend e) {
unsigned timeout;
t :> timeout;
timeout += 5000;
select {
case my_case(c, timeout);
case my_case(d, timeout);
case e :> int y:
e <: y + 2;
break;
}
}
在这里,选择函数在同一个select中使用不同的参数进行重用。