【问题标题】:Javascript thread-handling and race-conditionsJavascript 线程处理和竞争条件
【发布时间】:2014-02-23 09:29:25
【问题描述】:

假设我有如下代码:

var shared = 100;
function workWithIt(){
    shared += 100;
}

setTimeout(workWithIt, 500);
setTimeout(workWithIt, 500);

理想情况下,这段代码应该给变量shared加上200,之后是300。

但是,正如我从 c 了解到的,如果 += 操作被拆分为多个命令,则可能会产生一些影响。

可以说,这是函数的执行顺序:

setTimeout() --> create Thread A
setTimeout() --> create Thread B
wait 500ms

      **Thread A**              |        **Thread B**
--------------------------------+---------------------------------
var tmpA = shared;  //100       |
                                |   var tmpB = shared; //100
                                |   tmpB = tmpB+100;   //tmpB=200
                                |   shared = tmpB;
tmpA = tmpA+100;    //tmpA=200  |
shared = tmpA;                  |

在这种情况下,shared 现在的值是 200。

这可以在许多编程语言中发生,例如 c、c++、java、c#、... - 但它也可以在 Javascript 中发生吗?

或者更笼统地说:Javascript 如何处理其线程,何时在线程之间切换,是否有内置方法可用于处理竞争条件?

【问题讨论】:

  • JavaScript 是单线程的。
  • 但是你可以有几个并行的执行行,浏览器在它们之间切换。即使发生在同一个线程中,结果也是一样的。
  • 这不是“在它们之间切换”。定时器就像事件生成器,定时器函数在处理下一个事件(或定时器)之前运行完成。在您的示例中,一个功能或另一个将始终在另一个开始之前运行完成。
  • 是的。 alert() 函数本身是一个例外:在等待用户关闭警报时,浏览器允许运行一些事件处理程序(我不记得具体是哪些)。但是运行中的 JavaScript 代码不会被浏览器中断——这就是为什么计算密集型代码(或无限循环)会导致浏览器弹出一个对话框,询问用户是否要终止正在运行的代码。

标签: javascript multithreading synchronization thread-safety race-condition


【解决方案1】:

AFAIK,JS 中没有多线程。我稍微改变了你的例子,试图理解你的意思。

var shared = 100;
function workWithIt(a){
    shared += a||100;
    console.log(shared);
}

setTimeout(function(){workWithIt(5);}, 500);
setTimeout(function(){workWithIt(10);}, 500);
console.log(shared);

使用此代码,结果总是(在我的测试中):

100
105
110

这向我表明,这里没有混乱或随机甚至有趣的过程。在浏览器中使用 JS 创建赛车条件有一定的可能性,但您的计时示例并非如此。竞速只需要对执行顺序的可预测性进行细分。如果您将延迟从500 更改为Math.floor(500 * Math.random()),您可能会遇到赛车状况。

【讨论】:

    【解决方案2】:

    JavaScript 代码有一个显式的执行线程。您描述的场景永远不会在 JavaScript 中发生。定时器回调只是另一种事件,所有事件都被序列化,以便由浏览器 UI 线程的同一个核心事件循环顺序执行。

    因此,两个定时器事件不能同时处理,一个回调一个接一个。

    您仍然可以通过 Web Workers 在 JavaScript 中实现真正的并发。然而,一个 web worker 不能与另一个 web worker 或主线程共享任何对象。相反,网络工作者使用 JSON 序列化他们的状态对象,并使用postMessage 交换消息。所以,你的方案仍然是不可能的。

    但是,考虑另一种情况:

    var shared = 100;
    
    function workWithIt1(){
        shared += 100;
    }
    
    function workWithIt2(){
        shared = shared/2;
    }
    
    setTimeout(workWithIt1, 500);
    setTimeout(workWithIt2, 500);
    

    一旦两个超时都被触发,shared 会是 150 还是 100?可能是100,因为workWithIt1 超时首先排队。但是,我不会依赖这个事实,因为两个计时器具有相同的超时值500,并且计时器的实现可能是特定于浏览器的。您可能希望避免代码中出现类似的副作用。

    【讨论】:

      猜你喜欢
      • 2020-07-12
      • 2012-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多