注册 | 登录 | 设为首页 | 加入收藏
您当前的位置:飞翔学院-IT中国 → 编程开发AJAX → 文章内容

在AJAX程序中实现互斥揭秘

作者:佚名 来源:不详 发布时间:2007-12-10 22:07:35
Wallace变体

  在JavaScript中实现Lamport面包店算法的主要障碍在于缺少线程API。无法确定当前正在哪个线程上运行以及当前正在活动的线程数目,也无法将CPU释放给其他的线程,无法创建新的线程来管理其他线程。因此,无法查证如何将特定的浏览器事件(例如:单击按纽、可用的XML应答等)分配到线程。

  克服这些障碍的一种方法是使用Command设计模式。通过将所有应该进入临界区的逻辑以及所有启动该逻辑所需的数据一起放入到command 对象中,可以在负责管理command的类中重写面包店算法。该互斥类仅在没有其他临界区(封装为独立的command对象方法)在执行时调用临界区,就像它们各自运行在不同的虚拟线程中一样。JavaScript的setTimeout()机制用于将CPU释放给其他正在等待的command。

  为command对象假定一个简单的基类(见清单2中的Command),可以定义一个类(见清单3中的Mutex)来实现面包店算法的Wallace变体。注意,虽然可以通过很多方式在JavaScript中实现基类对象(为了简洁起见,这里使用一种简单的方式),但是只要各个command对象拥有某个惟一的id,而且整个临界区被封装在单独的方法中,那么任何对象模式都可以使用这种方法。

  清单2. 用于 Command 对象的简单基类

1 function Command() {
2  if (!Command.NextID) Command.NextID = 0;
3  this.id = ++Command.NextID;
4  // unsynchronized API
5  this.doit = function(){ alert("DOIT called"); }
6  this.undo = function(){ alert("UNDO called"); }
7  this.redo = function(){ this.doit(); }
8  // synchronized API
9  this.sDoIt = function(){ new Mutex(this,"doit"); }
10  this.sUnDo = function(){ new Mutex(this,"undo"); }
11  this.sReDo = function(){ new Mutex(this,"redo"); }
12 }

  Command类演示了三个临界区方法(见5-7行),但是只要预先将对该方法的调用封装在Mutex中(见9-11行),那么就可以使用任何方法。有必要认识到,常规方法调用(例如非同步的方法调用)与同步方法调用之间存在着重要的区别:具有讽刺意味的是,必须保证同步方法不同步运行。换句话说,当调用sDoIt()方法时,必须确保方法doit()还未运行,即使方法sDoIt()已经返回。doit()方法可能已结束,或者直到将来的某一时间才开始执行。也就是说,将对Mutex的实例化视为启动一个新的线程。

  清单3.作为类 Mutex实现的 Wallace 变体

1 function Mutex( cmdObject, methodName ) {
2  // define static field and method
3  if (!Mutex.Wait) Mutex.Wait = new Map();
4   Mutex.SLICE = function( cmdID, startID ) {
5    Mutex.Wait.get(cmdID).attempt( Mutex.Wait.get(startID) );
6   }
7   // define instance method
8   this.attempt = function( start ) {
9    for (var j=start; j; j=Mutex.Wait.next(j.c.id)) {
10     if (j.enter
11      || (j.number && (j.number < this.number ||
12      (j.number == this.number
13      && j.c.id < this.c.id))))
14      return setTimeout
15      ("Mutex.SLICE("+this.c.id+","+j.c.id+")",10);
16     }
17     //run with exclusive access
18     this.c[ this.methodID ]();
19     //release exclusive access
20     this.number = 0;
21     Mutex.Wait.remove( this.c.id );
22    }
23    // constructor logic
24    this.c = cmdObject;
25    this.methodID = methodName;
26    //(enter and number are "false" here)
27    Mutex.Wait.add( this.c.id, this );
28    this.enter = true;
29    this.number = (new Date()).getTime();
30    this.enter = false;
31   this.attempt( Mutex.Wait.first() );
32  }

  Mutex类的基本逻辑是将每个新的Mutex实例放入主等待清单,然后将其在等待队列中启动。因为每次到达“队首”的尝试都需要等待(除了最后一次),所以使用setTimeout来调度每次在当前尝试停止的位置启动的新尝试。到达队首时(见17行),便实现了互斥性访问;因此,可以调用临界区方法。执行完临界区后,释放互斥性访问并从等待清单中移除Mutex实例(见20-21行)。

  Mutex构造函数(见23-31行)记录其Command对象和方法名参数,然后寄存在一个运行中临界区的稀疏数组中(Mutex.Wait),这通过清单4中所示的Map类来实现。然后构造函数获得下一个编号,并在队尾开始排队。由于等待编号中的间隔或副本不存在问题,所以实际上使用当前的时间戳作为下一个编号。

  attempt()方法将初始伪代码中的两个wait循环组合成一个单独的循环,该循环直到队首时才对临界区失效。该循环是一种忙碌-等待循环检测方式,可以通过在setTimeout()调用中指定延迟量来终止该循环。由于setTimeout需要调用“无格式函数”,所以在第4-6行定义了静态帮助器方法(Mutex.SLICE)。SLICE在主等待清单中查找指定的Mutex对象,然后调用其attempt()方法,用start参数指定到目前为止其所获得的等待清单的长度。每次SLICE()调用都像获得了“一块CPU”。这种(通过setTimeout)适时释放CPU的协作方式令人想到协同程序。

  清单4. 作为 Map数据结构实现的稀疏数组

function Map() {
 this.map = new Object();
 // Map API
 this.add = function( k,o ){
  this.map[k] = o;
 }
 this.remove = function( k ){
  delete this.map[k];
 }
 this.get = function( k ){
  return k==null ? null : this.map[k];
 }
 this.first = function(){
  return this.get( this.nextKey() );
 }
 this.next = function( k ){
  return this.get( this.nextKey(k) );
 }
 this.nextKey = function( k ){
  for (i in this.map) {
   if ( !k ) return i;
   if (k==i) k=null; /*tricky*/
  }
  return null;
 }
}

更多内容请看PCdog.com--Ajax技术  Ajax框架与实例专题
上一页  [1] [2] [3]  下一页


  • 打印文档
  • 推荐好友
  • 返回顶部
  • 增大字体
  • 减少字体
关于本站 | 工作机会 | 合作网站 | 广告服务 | 市场合作| 联系我们 | 抽奖活动
版权所有: 武汉威俊科技有限公司 Copyright 2005-2007 www.ITCNW.COM All rights reserved