如何实现锁?

问题描述 投票:0回答:9

如何在 JavaScript 中实现相当于 C# 中的

lock
的功能?

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

用户单击按钮

B
B
引发 onclick 事件。如果
B
位于
event-state
中,则事件会在传播之前等待
B
位于
ready-state
中。如果
B
ready-state
中,
B
被锁定并设置为
event-state
,则事件将传播。当事件传播完成时,
B
设置为
ready-state

我可以看到如何完成类似的事情,只需从按钮中添加和删除类

ready-state
即可。然而,问题是用户连续两次单击按钮的速度比设置变量的速度快,因此这种锁定尝试在某些情况下会失败。

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

javascript events locking dom-events deadlock
9个回答
99
投票

Lock 在 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 解决方案来实现此目的。


46
投票

JavaScript 是,有极少数例外(某些版本的 Firefox 中的

XMLHttpRequest
onreadystatechange
处理程序)事件循环并发。所以在这种情况下你不必担心锁定。

JavaScript 有一个基于“事件循环”的并发模型。这个模型与其他语言(如 C 或 Java)中的模型有很大不同。

...

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

...

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

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

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


23
投票

我已经成功了互斥承诺

我同意其他答案,您可能不需要锁定您的案例。但在 Javascript 中并不需要锁定。访问不处理并发的外部资源时,您需要互斥。


14
投票

如果它对 2022 年以后的任何人有帮助,所有主要的浏览器现在都支持Web Locks API,尽管还处于实验阶段。

引用MDN中的例子:

await do_something_without_lock();

// Request the lock.
await navigator.locks.request('my_resource', async (lock) => {
  // The lock has been acquired.
  await do_something_with_lock();
  await do_something_else_with_lock();
  // Now the lock will be released.
});
// The lock has been released.

await do_something_else_without_lock();
  • 回调返回时锁自动释放
  • 锁的范围仅限于源 (
    https://example.com
    !=
    https://example.org:8080
    ),并且跨选项卡/工作人员工作。
  • 锁定请求排队(先到先得); (与其他一些语言不同,锁被随机传递给某个线程)
  • navigator.locks.query()
    可以用来查看什么有锁,以及队列中有谁来获取锁
  • 如果需要,可以使用
    mode="shared"
    来实现读写锁

6
投票

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

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


4
投票

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

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


2
投票

为什么不禁用该按钮并在完成活动后启用它?

<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>

快乐编码!!!


0
投票

根据我的情况对 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 调用,这两个函数将消耗相同的结果。


0
投票

锁在 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
})
© www.soinside.com 2019 - 2024. All rights reserved.