JavaScript线程处理和竞争条件

5
假设我有这样一段代码:
var shared = 100;
function workWithIt(){
    shared += 100;
}

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

理想情况下,这段代码应该将200加到变量shared上,之后它的值为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如何处理其线程?何时在线程之间切换,并且是否有内置方法可用于处理竞态条件?

3
JavaScript 是单线程的。 - Pointy
但是您可以并行拥有多个执行线程,浏览器在它们之间进行切换。即使它们发生在同一个线程中,结果也是相同的。 - maja
2
不是“在它们之间切换”。计时器就像事件生成器,计时器函数在下一个事件(或计时器)被处理之前运行到完成。在您的示例中,一个函数或另一个函数始终会在另一个函数开始之前运行到完成。 - Pointy
如果我启动了两个计时器,其中一个触发了警报,但如果另一个计时器先触发并且由于无限循环或耗时的ajax请求而没有完成执行,那么警报将永远不会被显示出来? - maja
1
是的。alert() 函数本身就是一个例外:在等待用户关闭警告框时,浏览器允许一些事件处理程序运行(我无法确切地回忆起哪些)。但是,运行 JavaScript 代码不会被浏览器中断 - 这就是为什么计算密集型代码(或无限循环)可能会导致浏览器弹出对话框询问用户是否要终止正在运行的代码的原因。 - Pointy
1
这是另一个好的StackOverflow问题。[链接](https://dev59.com/BHE85IYBdhLWcg3whT9r) - Pointy
2个回答

6

JavaScript代码只有一个明确的执行线程。你描述的情况在JavaScript中永远不会发生。定时器回调只是另一种事件,所有事件都由浏览器UI线程的同一核心事件循环进行序列化顺序执行。

因此,两个定时器事件不能同时处理,一个回调将在另一个之后发生。

您仍然可以通过Web Workers实现JavaScript的真正并发。但是,Web Worker无法与另一个Web Worker或主线程共享任何对象。相反,Web Worker使用JSON序列化其状态对象,并使用postMessage交换消息。因此,您的情况仍然是不可能的。

但是,请考虑另一种情况:

var shared = 100;

function workWithIt1(){
    shared += 100;
}

function workWithIt2(){
    shared = shared/2;
}

setTimeout(workWithIt1, 500);
setTimeout(workWithIt2, 500);

一旦两个超时时间都被触发,那么shared150还是100呢?它可能是100,因为workWithIt1的超时时间先排在队列前面。然而,我不会依靠这个事实,因为两个计时器都有相同的超时值500,计时器的实现可能与浏览器有关。您可能希望避免在代码中出现这样的副作用。


2
据我所知,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()) ,你会有一个赛车条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接