在JavaScript中是否需要使用互斥锁?

122

我看到了这个链接:在JavaScript中实现互斥

另一方面,我读到说JavaScript中没有线程,但这到底意味着什么?

当事件发生时,它们会在代码的哪里中断?

如果JS中没有线程,那么我需要在JS中使用互斥量吗?

具体而言,我想知道使用由setTimeout()XmlHttpRequestonreadystatechange调用的函数对全局可访问变量的影响。

7个回答

119

Javascript被定义为是一种可重入的语言,这意味着用户无需暴露线程,但在实现中可能会存在线程。像setTimeout()和异步回调函数需要等待脚本引擎休眠后才能运行。

也就是说,在事件中发生的所有事情都必须在下一个事件被处理之前完成。

话虽如此,如果您的代码执行某些操作,希望在异步事件触发时进行值不变,则可能需要使用互斥锁。

例如,如果您有一个数据结构,其中您点击一个按钮并发送XmlHttpRequest,该请求调用一个回调函数以破坏性方式更改数据结构,而您另外还有一个按钮直接更改同一数据结构,则当事件触发时和回调被执行时之间用户可以单击并更新数据结构,然后回调将丢失该值。

虽然您可以创建这样的竞争条件,但非常容易在代码中防止它,因为每个函数都是原子的。实际上,要创建竞争条件需要很多工作和一些奇怪的编码模式。


38
创建这个竞争条件并不难:比如,我在一个字段中有一个"onkeyup"事件,它会触发一个ajax调用到数据库中获取一些值。快速输入数据很容易导致结果出现顺序混乱的情况。 - thomasb

23

6
过去的回忆,但当多个标签访问相同的本地存储时,我遇到了需要使用互斥锁的情况。 - psp
3
WebWorkers不影响可重入性,因为它们不共享变量状态,只通过传递消息与主线程通信,而这些消息会触发事件。 - Alnitak
还需要更多更新...有SharedArrayBuffer - Sebastian Simon

12

是的,在访问在不同标签页/窗口之间共享的资源(如 localStorage)时,JavaScript 中可能需要使用互斥锁。

例如,如果用户打开了两个标签页,则以下简单代码是不安全的:

function appendToList(item) {
    var list = localStorage["myKey"];
    if (list) {
        list += "," + item;
    }
    else {
        list = item;
    }
    localStorage["myKey"] = list;
}

在localStorage项目被“获取”(访问器)和“设置”(变异器)之间的时间内,另一个标签可能已经修改了该值。这通常是不太可能的,但有可能 - 您需要自己判断在特定情况下任何争用所涉及的可能性和风险。

请参阅以下文章以获取更详细信息:


7

正如@william所指出的那样,如果您的代码执行某些操作,并且期望在异步事件被触发和回调被调用之间值不会改变,则可能需要互斥锁。

更进一步地,如果您的代码执行某些操作,并且期望在异步请求解决之前独占控制资源,则可能需要互斥锁。

一个简单的例子是,当您有一个按钮时,点击该按钮会触发一个ajax调用来在后端创建一个记录。您可能需要一些代码来保护您免受用户过度点击而创建多个记录。有许多方法可以解决这个问题(例如禁用按钮,在ajax成功后启用)。您还可以使用一个简单的锁:

var save_lock = false;
$('#save_button').click(function(){
    if(!save_lock){
        //lock
        save_lock=true;
        $.ajax({
            success:function()
                //unlock
                save_lock = false;  
            }
        });
    }
}

我不确定这是否是最佳方法,我很想看看其他人如何在javascript中处理互斥,但据我所知,这是一个简单的互斥锁,非常方便。


6
我不会把这称为互斥锁,至少不是传统意义上的,因为你没有两个线程同时运行在单一的块中。 - Ovesh
17
互斥锁(mutex)简单来说就是一个算法,它帮助"避免同时使用共享资源"的情况。虽然多线程创建了使用互斥锁的需求,但定义并没有说互斥锁仅适用于你所描述的情况。 - alzclarke
2
你对互斥锁的正式定义是正确的。但当人们在现实世界中谈论互斥锁时,这几乎不是他们所关心的内容。 - Ovesh
1
相信这段代码应该放在whilesetTimeout或者setInterval中,失败n次后需要用到clearInterval的重试和超时逻辑。如果保持不变,你只能绕过已加锁的代码。互斥体和共享对象的外部处理与实现本身同样重要。 - MrMesees
2
旧帖,但在我看来,只要读操作if(!save_lock)和写操作save_lock=true不是单个原子操作,那么多个线程将能够运行该块。需要一个“测试和设置”IMHO。 - xdhmoore
显示剩余4条评论

4

JavaScript是单线程的......虽然Chrome可能是一个新的生物(我认为它也是单线程的,但每个标签页都有自己的JavaScript线程......我没有详细研究过,所以不要引用我)。

然而,你需要担心的一件事是你的JavaScript如何处理多个ajax请求返回的顺序不同。因此,你真正需要担心的只是确保你的ajax调用以一种方式处理,即如果结果与你发送的顺序不同,它们不会互相干扰。

这也适用于超时......

当JavaScript变成多线程时,再考虑互斥锁等问题....


1
JavaScript这门语言可以实现多线程,但是浏览器嵌入JavaScript引擎只能同时运行一个回调函数(例如onload、onfocus和

-3

事件被触发,但 JavaScript 执行仍是单线程的。

我的理解是当事件触发时,引擎会停止当前的执行过程以运行事件处理程序。处理程序完成后,脚本执行将恢复。如果事件处理程序更改了某些共享变量,则恢复的代码将看到这些变化“突然出现”。

如果您想要“保护”共享数据,简单的布尔标志就足够了。


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