关于阻塞/非阻塞,同步与异步
阻塞调用:调用某个函数时,需要等待其返回一个正确的结果(这个结果是调用者所希望的),不等到这个结果就不往下执行。
非阻塞调用:调用某个函数时,无论有没有结果或者结果是不是调用者想要的,程序都会继续往下执行。
同步:当调用者调用某个函数时需要等待一个结果,如果没有结果,那么如果是调用者主动去等待这个结果。
异步:当调用者调用某个函数时需要等待一个结果,如果没有结果,那么调用者不去主动等待这个结果,而是被动的去等待结果,当正确结果来临的时候就通知调用者去获取这个结果。
曾在其他博主博文中看过一个例子非常形象:
老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。
普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
所谓阻塞非阻塞,仅仅对于老张而言。
立等的老张,阻塞;看电视的老张,非阻塞。
情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
由此衍生出一个单片机状态机的阻塞调用和非阻塞调用。
单片机状态机伪代码:
enum fsm
{
fsm_doing, /* 单片机状态机还未执行完毕 */
fsm_ok, /* 单片机的整个状态执行完毕 */
};
/* 例如某个流程图需要一整个流程执行完毕才会有一个最终的结果,外部调用者只需要等待这个结果即可,以烤肉为例子 */
enum fsm WaitBarbecue(void)
{
static enum
{
START,
BUY_MEAT, /* 买肉 */
CUT_MEAT, /* 切肉 */
ROAST_MEAT, /* 烤肉 */
OVER,
} WAITBARBRCUE_STA = START;
switch (WAITBARBRCUE_STA):
{
case START:/* 做一些变量初始化的工作 */
WAITBARBRCUE_STA = BUY_MEAT;
break;
case BUY_MEAT:/* 做买肉的任务 */
WAITBARBRCUE_STA = CUT_MEAT;
break;
case CUT_MEAT:/* 做切肉的任务 */
WAITBARBRCUE_STA = ROAST_MEAT;
break;
case ROAST_MEAT:/* 做烤肉的任务 */
WAITBARBRCUE_STA = OVER;
break;
case OVER:/* 做进入下一次烤肉的准备 */
WAITBARBRCUE_STA = START;
return fsm_ok;
}
return fsm_doing;
}
/* 调用者 */
void main(void)
{
/* 系统初始化 */
while(1)
{
/* ①阻塞调用 */
while(WaitBarbecue() == fsm_doing); /* 一直等待烤肉完成 */
/* ②非阻塞调用 */
WaitBarbecue(); /* 只调用一次,不管此时处于什么阶段,调用完立马执行下面的代码 */
}
}
由上面可知可阻塞状态机的使用方法,那么可阻塞状态机的有什么好处呢?
好处就是可以进行可阻塞状态机的无线套娃调用,如:
/* 我要进行减肥,有三个过程:举铁,跑步,拉伸
举铁:可分为举杠铃30下,举哑铃30下,可作为一个状态机
跑步:可分为快跑10分钟,慢跑30分钟,可作为一个状态机
拉伸:可分为拉手10分钟,拉腿10分钟,可作为一个状态机
*/
enum fsm LoseWeight(void) /* 减肥状态机*/
{
static enum
{
START,
LIFT_IRON, /* 举铁 */
RUN, /* 跑步 */
STRETCH, /* 拉伸 */
OVER,
} LOSEWEIGHT_STA = START;
switch (LOSEWEIGHT_STA ):
{
case START:/* 初始化 */
LOSEWEIGHT_STA = LIFT_IRON;
break;
case LIFT_IRON:/* 举铁 */
while(LiftIron() == fsm_doing); /* 等待举铁完成 */
LOSEWEIGHT_STA = RUN;
break;
case RUN: /* 跑步 */
while(Run() == fsm_doing); /* 等待跑步完成 */
LOSEWEIGHT_STA = STRETCH;
break;
case STRETCH: /* 拉伸 */
while(Stretch() == fsm_doing); /* 等待跑步完成 */
LOSEWEIGHT_STA = OVER;
break;
case OVER:
LOSEWEIGHT_STA = START;
return fsm_ok;
}
return fsm_doing;
}
以上纯属个人见解,有误之处还望大家指正。