如何在 JavaScript 中实现锁

Posted

技术标签:

【中文标题】如何在 JavaScript 中实现锁【英文标题】:How to implement a lock in JavaScript 【发布时间】:2011-07-17 19:55:47 【问题描述】:

如何在 javascript 中实现与 C# 中的 lock 等效的东西?

所以,为了解释我的想法,一个简单的用例是:

用户点击按钮BB 引发 onclick 事件。如果Bevent-state 中,则事件在传播之前等待Bready-state 中。如果Bready-state 中,B 被锁定并设置为event-state,则事件传播。事件传播完成后,B 设置为 ready-state

我可以看到如何完成类似的事情,只需从按钮中添加和删除类ready-state。但是,问题是用户连续两次单击按钮的速度比设置变量的速度要快,因此这种锁定尝试在某些情况下会失败。

有谁知道如何在 JavaScript 中实现一个不会失败的锁?

【问题讨论】:

将不胜感激考虑并行 JS 执行(即像在 IE9 中)的答案。 @smart 所以你想暂时暂停当前的事件传播,直到前一个事件的传播完成。我不认为那可以做到。您可以这样做:丢弃该事件,然后在前一个事件完成传播时触发另一个事件。 @OrangeDog - 我只听说 IE9 尝试使用专用内核进行编译,没有关于并行执行,你能引用一个来源吗? @Brandon - 正如您所建议的,这可能意味着与渲染器平行,而不是与自身平行。 相关:***.com/questions/6266868/… 【参考方案1】:

锁在 JS 中是一个有问题的想法,它旨在实现无线程且不需要并发保护。您希望合并对延迟执行的调用。我遵循的模式是使用回调。像这样的:

var functionLock = false;
var functionCallbacks = [];
var lockingFunction = function (callback) 
    if (functionLock) 
        functionCallbacks.push(callback);
     else 
        $.longRunning(function(response) 
             while(functionCallbacks.length)
                 var thisCallback = functionCallbacks.pop();
                 thisCallback(response);
             
        );
    

您也可以使用 DOM 事件侦听器或 pubsub 解决方案来实现这一点。

【讨论】:

能否请您提供参考文档来证明您的声明“JS 旨在实现无线程且不需要并发保护”?我想了解更多相关信息。 +1 - 鉴于 Node.js(JavaScript Web 服务器)是为利用 JavaScript 的单线程和回调机制而构建的,我同意您不必担心锁定属性将没有竞争条件。 $.longRunning 来自哪个库?它不在(当前)jQuery 中。 什么时候设置functionLock? “旨在实现无线程且不需要并发的 JS”是可疑的。 JS 不使用抢占式并发,但允许您使用协作并发(基于回调或异步/等待)。在使用这些时,您肯定会遇到竞争条件,并且可能希望使用互斥抽象来避免它们。【参考方案2】:

JavaScript 与 exceptions(在某些版本的 Firefox 中为 XMLHttpRequest onreadystatechange 处理程序)event-loop concurrent 是一样的。所以在这种情况下你不必担心锁定。

JavaScript 有一个基于“事件循环”的并发模型。该模型与 C 或 Java 等其他语言中的模型完全不同。

...

JavaScript 运行时包含一个消息队列,它是要处理的消息列表。每条消息都关联一个功能。当堆栈为空时,从队列中取出一条消息并进行处理。 处理包括调用相关函数(并因此创建一个初始堆栈帧)。当堆栈再次变空时,消息处理结束。

...

每条消息都在处理任何其他消息之前被完全处理。这在推理您的程序时提供了一些很好的属性,包括这样一个事实:每当一个函数运行时,它不能被抢占并且会在任何其他代码运行之前完全运行(并且可以修改函数操作的数据)。 这与 C 不同,例如,如果一个函数在一个线程中运行,它可以随时停止以在另一个线程中运行其他代码。

此模型的一个缺点是,如果一条消息需要很长时间才能完成,Web 应用程序将无法处理用户交互,例如单击或滚动。浏览器通过“脚本运行时间过长”对话框来缓解这种情况。 一个好的做法是缩短消息处理时间,并在可能的情况下将一条消息缩减为多条消息。

有关事件循环并发的更多链接,请参阅E

【讨论】:

并且在javascript中使用Web Workers(non UI threads),每个对象clones在发送到另一个工作线程(线程)或UI线程之前,然后对象在不同的​​线程中有不同的实例,每个线程都有自己的事件循环,最后我同意迈克的回答作为一个有用的答案。谢谢迈克。【参考方案3】:

我已经成功了mutex-promise。

我同意您可能不需要锁定您的情况的其他答案。但是,永远不需要锁定 Javascript 是不正确的。在访问不处理并发的外部资源时,您需要互斥。

【讨论】:

感谢非常需要的东西。【参考方案4】:

锁是多线程系统所需的概念。即使使用工作线程,消息也是在工作线程之间按值发送的,因此不需要锁定。

我怀疑您只需要在按钮之间设置一个信号量(标记系统)。

【讨论】:

你有任何用 JavaScript 实现的“信号量/标记系统”的示例或资源吗? PLenty 就在这里,即***.com/questions/4194346/… James 我在您发布的链接中没有看到任何信号量示例。我需要阻止对外部服务器的调用,因为它们每秒只允许一个请求,所以我需要一个标记示例来阻止调用,直到我的 for next 循环内经过的时间过去,而不跳过每个循环的请求。 嗨@克劳斯。有两件事浮现在脑海中,根据您的堆栈,您可以只使用像 Axios 这样的库,它似乎支持速率限制插件,或者您可以创建一个基于域的速率限制请求的网络工作者。 感谢您的回复。我通过在处理结束时结合 setInterval 调用 click 事件来解决它。将间隔设置为 1000 毫秒我现在遵守主机服务器对请求的限制,并且在返回响应之前它不再退出 Ajax 调用。【参考方案5】:

为什么不禁用按钮并在完成事件后启用它?

<input type="button" id="xx" onclick="checkEnableSubmit('true');yourFunction();">

<script type="text/javascript">

function checkEnableSubmit(status)   
  document.getElementById("xx").disabled = status;


function yourFunction()

//add your functionality

checkEnableSubmit('false');


</script>

编码愉快!!!

【讨论】:

【参考方案6】:

根据我的情况,对 JoshRiver 的回答有所补充;

var functionCallbacks = [];
    var functionLock = false;
    var getData = function (url, callback) 
                   if (functionLock) 
                        functionCallbacks.push(callback);
                    else 
                       functionLock = true;
                       functionCallbacks.push(callback);
                        $.getJSON(url, function (data) 
                            while (functionCallbacks.length) 
                                var thisCallback = functionCallbacks.pop();
                                thisCallback(data);
                            
                            functionLock = false;
                        );
                    
                ;

// Usage
getData("api/orders",function(data)
    barChart(data);
);
getData("api/orders",function(data)
  lineChart(data);
);

只有一个 api 调用,这两个函数将消耗相同的结果。

【讨论】:

【参考方案7】:

锁在 JS 中仍然有用。根据我的经验,我只需要使用锁来防止垃圾邮件点击进行 AJAX 调用的元素。 如果您为 AJAX 调用设置了加载程序,那么这不是必需的(以及在单击后禁用按钮)。 但无论哪种方式都是我用于锁定的方式:

var LOCK_INDEX = [];
function LockCallback(key, action, manual) 
    if (LOCK_INDEX[key])
        return;
    LOCK_INDEX[key] = true;
    action(function ()  delete LOCK_INDEX[key] );
    if (!manual)
        delete LOCK_INDEX[key];

用法:

手动解锁(通常用于 XHR)

LockCallback('someKey',(delCallback) =>  
    //do stuff
    delCallback(); //Unlock method
, true)

自动解锁

LockCallback('someKey',() =>  
    //do stuff
)

【讨论】:

【参考方案8】:

这是一个简单的锁机制,通过闭包实现

const createLock = () => 

    let lockStatus = false

    const release = () => 
        lockStatus = false
    

    const acuire = () => 
        if (lockStatus == true)
            return false
        lockStatus = true
        return true
    
    
    return 
        lockStatus: lockStatus, 
        acuire: acuire,
        release: release,
    


lock = createLock() // create a lock
lock.acuire() // acuired a lock

if (lock.acuire())
  console.log("Was able to acuire");
 else 
  console.log("Was not to acuire"); // This will execute


lock.release() // now the lock is released

if(lock.acuire())
  console.log("Was able to acuire"); // This will execute
 else 
  console.log("Was not to acuire"); 


lock.release() // Hey don't forget to release

【讨论】:

以上是关于如何在 JavaScript 中实现锁的主要内容,如果未能解决你的问题,请参考以下文章

ReentrantLock 详解

如何跨 SwiftUI 应用程序跟踪所有触摸

自定义锁线程 python

如何在 javascript 中实现 Random.nextDouble()?

如何在 Wordpress 中实现 javascript

如何在javascript中实现区域/代码折叠