JavaScript需要互斥吗?


104

我看过这个链接:在JavaScript中实现互斥。另一方面,我已经读到javascript中没有线程,但这到底是什么意思?

当事件发生时,它们可以在代码中的何处中断?

并且,如果JS中没有线程,是否需要在JS中使用互斥锁?

具体来说,我想知道如何使用功能由所谓的影响setTimeout()XmlHttpRequestonreadystatechange对全局访问的变量。


1
不,没有互斥体或javascript中的任何其他并发控制工具。请参见为什么没有javascript中的并发控制工具
Uzair Farooq 2015年

Answers:


101

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

这意味着事件中发生的所有事情都必须先完成,然后才能处理下一个事件。

话虽如此,如果您的代码执行了某些操作,希望在触发异步事件和调用回调之间不更改其值,则可能需要使用互斥锁。

例如,如果您有一个数据结构,在其中单击一个按钮,然后发送一个XmlHttpRequest来调用回调,则该XmlHttpRequest以破坏性的方式更改数据结构,而在事件发生之间,您有另一个按钮直接更改相同的数据结构。触发后,执行回调时,用户可能在回调之前单击并更新了数据结构,这可能会丢失值。

尽管您可以创建这样的竞争条件,但是很容易在代码中避免这种情况,因为每个函数都是原子的。实际上,这将需要大量工作并需要一些奇怪的编码模式才能创建竞争条件。


13
创建这种竞争条件并不难:例如,我在一个字段中有一个“ onkeyup”事件,该事件触发对数据库的ajax调用以获取一些值。快速键入数据很容易导致混乱的结果。
thomasb

19

这个问题的答案虽然在给出时是正确的,但有些过时了。而且,如果查看不使用webworkers的客户端javascript应用程序,则仍然正确。

文章对webworkers:
使用JavaScript中多线程webworkers
的Mozilla上webworkers

这清楚地表明,通过网络工作者的javascript具有多线程功能。关于这个问题,JavaScript是否需要互斥体?我不确定。但是,这个stackoverflow帖子似乎很相关:
N个异步线程的互斥


3
从过去开始爆炸,但是当多个选项卡访问相同的本地存储时,我遇到了互斥的需求
PSP 2014年

3
WebWorkers不会影响重新进入,因为它们不共享任何变量状态,并且仅通过传递触发事件的消息来与主线程进行通信。
Alnitak

9

正如@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中的互斥,但据我所知,这是一个简单的互斥体,非常方便。


4
我几乎不会将此称为互斥体,至少不是传统意义上的互斥体,因为您随时都不会在单个块的上下文中运行两个线程。
Ovesh

10
互斥锁只是一种算法,可以帮助“避免同时使用公共资源”。尽管多线程需要互斥量,但定义中没有任何内容表明互斥量特定于您所描述的情况。
alzclarke 2011年

1
您对互斥量的正式定义是正确的。但是,当人们谈论现实世界中的互斥时,这几乎不是考虑的问题。
奥韦什

这无法正常工作。不幸的是,重复单击仍然会触发ajax调用。还有其他想法吗?
Mohammed Shareef C

1
相当确定,应该在n次失败后将其包装为whilewith setTimeoutsetIntervalwith clearInterval,以便您具有重试和超时逻辑。保留原样意味着您将绕过已锁定的代码。互斥锁和共享对象的外部处理与实现本身一样重要。
MrMesees

6

JavaScript是单线程的...尽管Chrome可能是一种新的野兽(我认为它也是单线程的,但是每个选项卡都有它自己的JavaScript线程...我没有对其进行详细研究,所以请不要引用我那里)。

但是,您需要担心的一件事是JavaScript如何处理以不同发送顺序返回的多个ajax请求。因此,您真正需要担心的是,如果结果返回的顺序与您发送的顺序不同,请确保ajax调用的处理方式不会使对方步履蹒跚。

这也超时了...

当JavaScript增长为多线程时,则可能会担心互斥锁等问题。


4

是的,访问选项卡/窗口之间共享的资源(例如localStorage)时,JavaScript中可能需要互斥锁。

例如,如果用户打开了两个选项卡,则如下所示的简单代码是不安全的:

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

在localStorage项目为“ get”和“ set”之间,另一个选项卡可能已修改了该值。通常不太可能,但有可能-您需要自己判断与您的特定情况下任何竞争相关的可能性和风险。

有关更多详细信息,请参见以下文章:


2

JavaScript是一种语言,可以根据需要使用多线程,但是javascript引擎的浏览器嵌入一次只能运行一次回调(onload,onfocus,<script>等...)。威廉(William)建议使用Mutex在注册和接收回调之间进行更改的建议不应该从字面上理解,因为您不想阻止介入的回调,因为用于解锁该回调的回调将被阻塞在当前回调之后!(哇,英语在谈论线程方面很烂。)在这种情况下,如果设置了一个标志,则可能要沿字面意义或使用setTimeout()之类的方法重新分配当前事件。

如果您使用的是JS的不同嵌入,并且一次执行多个线程,则可能会更加麻烦,但是由于JS可以轻松使用回调并在属性访问中锁定对象的方式,显式锁定几乎没有必要。但是,如果为使用多线程的通用代码(例如游戏脚本)设计的嵌入也没有给出一些明确的锁定原语,我会感到惊讶。

抱歉!


0

事件已发出信号,但JavaScript执行仍然是单线程的。

我的理解是,当事件被信号通知时,引擎将停止其正在执行的事件以运行事件处理程序。处理程序完成后,将继续执行脚本。如果事件处理程序更改了一些共享变量,则恢复的代码将看到这些更改显得“出乎意料”。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.