16

我有一个 Web 应用程序,其中有一个不断倒计时的计时器。同时,客户端经常与服务器检查以查看是否已将更多时间添加到计时器。代码看起来像这样:

function tick() {
    // This function is called once every second
    time -= 1;
    redisplay(time);
};
function update(newtime) {
    // This function is called whenever the ajax request
    // to the server yields a new time
    time = newtime;
};

当然,它比这要复杂一些,但是您可以看到固有的竞争条件。如果 update 和 tick 函数都试图同时修改time怎么办?

坦率地说,我对 JavaScript 的了解还不够多,无法理解如何处理这种并发问题:有没有一种简单的方法可以做到这一点,或者如果没有,有人可以指出我可以了解更多信息的资源吗?

谢谢你。

4

5 回答 5

16

您没有竞争条件,因为 Javascript 不会同时执行。

每次从异步操作(AJAXsetTimeout等)触发回调时,该回调必须完成执行,然后才能调用来自另一个异步操作的另一个回调。因此,如果update()正在运行,则没有其他 Javascript。完成update()后,可以触发来自异步操作的其他回调(例如,tick())。有趣的是,这也是为什么setTimeout不能保证在达到超时的精确时刻执行它的同类:其他一些 javascript 可能会阻止回调的执行。

查看http://ejohn.org/blog/how-javascript-timers-work/了解有关这一切如何工作的一些有用信息。

于 2010-01-16T19:36:49.197 回答
8

Javascript是单线程的。没有竞争条件。time = newtime;这两行在javascript中没有办法在time -= 1;执行过程中重叠。事实上,这两个功能是保证不重叠的。其中一个将运行,然后另一个将运行。

于 2010-01-16T19:35:47.513 回答
0

我错了:这并不能解决问题!(代码后的解释)

NEWTIME = false;
function tick() {
    // This function is called once every second
    if (NEWTIME) {
        time = NEWTIME;
        NEWTIME = false;
    }
    time -= 1;
    redisplay(time);
};
function update(newtime) {
    // This function is called whenever the ajax request
    // to the server yields a new time
    NEWTIME = newtime;
};

这个错误解决方案的问题在于,这样做您只需将竞争条件问题从 variabletime移到 variable NEWTIME

试想一下:现在执行tick到达并执行该行time = NEWTIME;,在继续之前,更新被调用并NEWTIME获取一个值X。现在刻度执行继续执行NEWTIME = false;。这样你就失去了update() 调用的X价值和效果!NEWTIME

于 2010-01-16T17:37:18.927 回答
-1

这个问题需要一些信号量。在前一个完成后,我为发送 ajax 做了很多事情。你的情况有点像;)

尝试类似的东西。它应该忽略所有与 ajax 回调冲突的减少时间的尝试。

window.TimeKeeper = new function(){
this.time=0; //initial value, whatever
this.isopen=true;

this.decrement = function(){ 
 if(this.isopen){
  this.time--;
  redisplay(this.time);
  }
 }
this.set = function(val){
 if(this.isopen){
  this.isopen=false;
  this.time=val;
  this.isopen=true;
  } else {
  //another AJAX callback is modifying time. 
   //You can enqueue current value and put it later
  }
 }

}

function tick() {
    TimeKeeper.decrement();

};
function update(newtime) {
    TimeKeeper.set(newtime);
};

还有一件事- setTimeout 就像一个新线程一样工作,我希望浏览器执行同步 mem 访问,因此在递减之前检查值是否没有增长可能就足够了。但上述方案更加灵活和安全。

还有一个小技巧——避免过于频繁地使用 AJAX 进行查询——这可能会导致额外的问题——比如请求返回的顺序与发送的顺序不同,以及每分钟过多的 ajax 会导致 Firefox 内存使用量增加;)

于 2010-01-16T19:14:09.323 回答
-2

只要您在当前时间上再增加几秒钟,就可以了。

而不是做time = newtime;尝试time += newtime;这样你就不会失去你担心的第二个。

尽管如此,你最多只会损失一秒钟。

于 2010-01-16T17:17:43.947 回答