在上一单元中,我们制作了一个简单的事件循环框架。
int main()
{
int event;
eventHandler func;
while(1){
event = Event(); //事件检测
if(event != -1){
func = findEventHandler(event); //事件匹配,分发
func(); //事件处理
}
}
}
我们发现,如果某一个事件处理函数出现了阻塞,将会导致整个事件循环阻塞,进而其他事件也无法处理。其实,这是典型的并发问题,解决这种问题,有三种编程模型
- 回调模
- 协程模型
- 多线程、多进程模型
回调模型
回调实现LED闪烁功能,点亮3s,熄灭3s
void LedFlash() //led闪烁,点亮3s,熄灭3s
{
if(LED == ON)
LED = OFF;
else
LED = ON;
setTimerEvent(3000,LedFlash); //定时器,每隔3s调用一次LedFlash函数
}
如果是点亮3s,熄灭5s呢?
void LedOn()
{
LED = ON;
}
void LedOff()
{
LED = OFF;
}
void LedFlash() //led闪烁,点亮3s,熄灭5s
{
if(LED == ON){
LedOff(); //熄灭LED灯
setTiemrEvent(5000,LedOn); //5s后,再点亮LED灯
}
else{
LedOn(); //点亮LED灯
setTiemrEvent(3000,LedOff); //3s后,再熄灭LED灯
}
}
我们看到,LedFlash的逻辑已经比较复杂,但我们还能直观的观察到代码逻辑。可如果是,先点亮3s,再熄灭5s,然后点亮5s,再熄灭3s呢?
static int LED;
static int FLASH; //第几次闪烁(第一次或第二次)
void LedOn()
{
LED = ON;
}
void LedOff()
{
LED = OFF;
}
void LedFlash() //led闪烁,点亮3s,熄灭5s
{
if(FLASH == first){ // 第一次闪烁
if(LED == ON){
LedOff(); //熄灭LED灯
setTiemrEvent(5000,LedOn); //5s后,再点亮LED灯
}else{
LedOn(); //点亮LED灯
FLASH = sencond; //你能在5s之内想明白这个变量应该怎么赋值么??
setTiemrEvent(3000,LedOff); //3s后,再熄灭LED灯
}
}
if(FLASH == second){ // 第二次闪烁
if(LED == ON){
LedOff(); //熄灭LED灯
setTiemrEvent(3000,LedOn); //3s后,再点亮LED灯
}else{
LedOn(); //点亮LED灯
Flash = first //你能在1min之内想明白这个变量应该怎么赋值么??
setTiemrEvent(5000,LedOff); //5s后,再熄灭LED灯
}
}
}
首先,代码量陡增,维护就会比较麻烦,代码逻辑也非常不直观
其实,在上面的编程过程中,我们一直用到了状态机的解决方案,只不过没有明确的表达出来,现在我们根据状态机的思路,再重新看一便上面的代码逻辑
- 先点亮3s, 第一次闪烁FLASH=first,当前LED灯是熄灭状态LED=OFF STATE = 0x10
- 再熄灭5s, 第一次闪烁FLASH=first,当前LED灯是点亮状态LED=ON STATE = 0x11
- 然后点亮5s, 第二次闪烁FLASH=second,当前LED灯是熄灭状态LED=OFF STATE = 0x20
- 再熄灭3s 第二次闪烁FLASH=sencond,当前LED灯是点亮状态LED=ON STATE = 0x21
static int STATE = 0x10;
void LedFlash()
{
switch(STATE){
case 0x10:
LED = ON; //点亮LED灯
STATE = 0x11; //切换状态
setTimerEvent(3000,LedFlash);
break;
case 0x11:
LED = OFF;
STATE = 0x20; //切换状态
setTimerEvent(5000,LedFlash);
break;
case 0x20:
LED = ON;
STATE = 0x21; //切换状态
setTimerEvent(5000,LedFlash);
break;
case 0x21:
LED = OFF;
STATE = 0x10; //切换状态
setTimerEvent(3000,LedFlash);
break;
}
}
虽然我们改善了代码逻辑,但是本质上来讲,回调模型把正常逻辑的执行流程做了切割,逻辑不自然,但其模型设计简单,效率高
其实对于上面的需求,我们更希望看到这样的代码
void LedFlash()
{
LedOn();
delay(3); //点亮3s
LedOff();
delay(5); //熄灭5s
LedOn();
delay(5); //点亮5s
LedOff();
delay(3); //熄灭3s
}
看到这种代码,大家可能会第一时间想到线程、进程模型。这里提醒一下,下一单元我们从协程切入。
这是一个免费,开源的教程,如果你喜欢可以转发,也可以打赏奖励。 欢迎关注微信公众号小站练兵

网友评论