利用AJAX技术实现Web开发中的互斥技术(1)(2)
Mutex构造器(第23~31行)记录它的Command对象和方法名参数,然后把它自己注册到一个正在进行的(Mutex.Wait)临界区稀疏数组,这是经由Map类实现的,如列表4所示。然后,它得到“下一个序号”并开始在最后一行等待。由于等待序号中不存在间断和重复的情况,所以当前的时间戳实际上被用作“下一个”序号。
attempt()方法把原始伪码中的两个等待循环结合到单个循环(直到等到列表最前端才进入临界区中)。这个循环是一种“忙等待”形式的查询,通过以指定的延迟时间来调用setTimeout()即可以“打破”此等待。既然setTimeout要求调用一个“普通函数”而不是一个对象方法,那么在第4~6行定义了一个静态的助理方法(Mutex.SLICE)。SLICE在顾客等待列表中定位指定的Mutex对象并且调用它的attempt()方法(它的开始参数用于指定,到目前为止,它进入到等待列表已有多长时间)。每一个SLICE()调用都相当于得到一个“CPU时间片”。这种移交CPU控制权的协作方式(经由setTimeout)以前被称作协同程序。
|
列表4.实现为一个Map数据结构的稀疏数组
五、RIA集成
既然Mutex能够处理动态数目的线程(虚拟的或非虚拟的),那么我们可以得出这样的结论:既然“浏览器对每个浏览器事件都赋予一个独立的线程”,那么,我们无法知道一个实际线程的ID。一个类似的简化的假设是,每个complete事件处理器都组成一个完整的临界区。这样的话,每个事件处理器函数就可以被转换成一个Command对象,并且可以使用Mutex来调用和管理它们。当然,如果代码还没有被整洁地组织到事件处理函数中,那么必须对之进行重构。换句话说,不是直接在HTML事件属性(例如,onclick='++var')中实现编码逻辑,而是定义并调用事件处理函数(例如,onclick='FOO()'和函数FOO(){++var;})。
列表5.具有非同步事件处理器的示例web页面
如列表5所示,假定共有3个事件处理器函数―它们都操作共同的数据。它们分别处理一个page-load事件,一个button-click事件和一个request-reply(接收XML请求并响应)事件。page-load事件处理器启动某种异步数据请求。它指定request-reply事件处理器―由它来处理接收的数据并把它加载到一个共享数据结构中。button-click处理器也影响这个共享数据结构。为防止这些事件处理器发生冲突,可以把它们转换成Command对象并使用Mutex来激活,如列表6所示。(假定Map和Mutex都包含在JavaScript包括文件mutex.js中)。注意,虽然可以使用良好的类继承机制来实现Command子类,但是这里的代码只展示了一种最小实现―仅使用了全局变量NEXT_CMD_ID。
列表6.被转换为同步事件处理器的Web页面
三个事件处理器函数都被转换以实现通过Mutex调用其原先的逻辑(现在,每个函数都被包装到Command类中)。每个Command类定义唯一的ID和一个包含临界区逻辑的方法,从而实现Command接口要求。
六、结论
随着AJAX技术和RIA技术的出现,Web开发者尝试使用与以前构造“胖”GUI客户端相同的设计模式(例如,模型-视图-控制器)来构建复杂的动态用户接口。通过把视图和控制器用模块化定义,每一部分都有其各自的事件和事件处理器(但共享共同的数据模型),这将会成倍地提高冲突的可能性。通过把事件处理逻辑封装到Command类中,我们不仅可以使用Wallace改进算法,而且可以为程序提供丰富的“撤消/恢复”功能,脚本接口以及单元测试工具。
参考信息
•本文中的示例共包含5个文件。其中包括被省略的细节―Web页面能够直接在浏览器中执行而不要求一个服务器连接。
•另外一个JavaScript框架(Gravy)也使用了本文中相应的技术,你可以查看或下载之,这个链接还提供了完整的JsDoc参考文档。Gravy支持把所有的应用程序功能都放在浏览器端的JavaScript中,应用程序仅仅需要存取服务器来实现数据库CRUD操作。