从Scratch 2学到的一些编程思想
所有程序都是事件驱动的
mingdu.zheng at gmail dot com
Scratch是麻省理工大学(MIT)为8~16岁的孩子开发的编程工具。Scratch的基本原理是为所选的角色编写脚本使其产生互动效果。编写脚本的方式则非常特别,将一些像积木一样的小方块拖拽到脚本区并按一定的结构进行组织就可以,完全没有coding的过程,全是拖拽的过程,有些地方需要修改一下参数,那么输入一些数字或者从下拉菜单选择一个合适的项。脚本可以有很多段,每一段的开头是一个事件块,事件可以是单击Scratch中的绿色旗帜,可以是某个按键消息,还可以是其它角色发出的事件消息。当某个事件发生后,就开始执行角色的脚本,不同的事件执行不同的脚本,不同角色对相同的事件也会有不同的响应。
在GUI编程当中,事件是个基本概念,GUI程序是基于事件驱动的。而我想到的是:计算机中的一切程序都是基于事件驱动的,只不过“事件“这个名词被许多其它类似的词汇所代替。中断(interrupt)是计算机外围设备向CPU发出的事件,信号量(semaphore)是中断服务例程向进程发出的事件,信号(signal)是进程A发给进程B的事件,请求(request)是客户端发给服务器的事件。而所有事件的源头则是人,如果没有人按下电源按钮(这也是个事件),然后通过键盘或鼠标输入一些内容,计算机只是静静地摆在那里一动不动。
事件可以简单地分为两种,原始事件和派生事件。原始事件和派生事件是相对而言的,对最底层的系统软件而言,中断是原始事件(系统复位也是一个特殊的中断事件,回想一下,复位向量也是中断向量表中的一员,而且通常是最前面的那个)中断服务例程根据中断号以及当前的状态产生信号量,这个信号量就是中断衍生出来的派生事件。同一个原始事件可以根据状态的不同衍生出很多不同的派生事件,不同的原始事件也可以衍生出相同的派生事件。
程序在执行同步IO等待数据就绪时,就是在等待一个事件。当数据请求被完成后,外设向CPU发出事件,CPU处理外设事件并衍生出唤醒程序的事件。程序执行异步IO时,当数据请求被完成后,外设向CPU发出事件,CPU处理外设事件并执行程序注册的回调函数。
有事件就有回调函数,在GUI编程中,这是很普遍的概念。在其它方面也是如此,如果事件是中断,那么中断服务例程不就是中断的回调函数吗?如果事件是信号量,那么等待信号量后面的那部分代码不就是信号量的回调函数吗?事件和回调函数的关系就是事件的产生将导致回调函数被执行。不要拘泥于回调函数是个函数,可以将回调函数理解为一段代码。函数本来就是一段代码。
事件还经常和状态机在一块,因为同一个事件在不同的状态下,会有不同的处理措施。状态机可以分为显式状态机和隐式状态机,显式状态机有一个明确的状态变量,并由程序显式地控制状态的变更,而隐式的状态机,没有明确的状态变量,但是会通过其它变量来标识状态。例如对于GUI程序,当产生一个鼠标点击事件时,会将坐标所在的窗口句柄传递过来,这个窗口句柄就是一个隐式的状态变量,由底层的GUI层来实现这个状态变量的变更。
回想起以前了解到的国人开发的都江堰操作系统,这是一个基于事件调度的操作系统,前几年听到这个概念的时候不是很理解,现在终于明白了,既然所有程序都是事件驱动的,那么操作系统调度事件来完成工作也就很自然了。