我看过这个链接:Implementing Mutual Exclusion in JavaScript。 另一方面,我已经读过javascript中没有线程,但究竟是什么意思呢?
当事件发生时,代码可以在哪里中断?
如果JS中没有线程,我是否需要在JS中使用互斥锁?
具体来说,我想知道使用setTimeout()
和XmlHttpRequest
onreadystatechange
调用的函数对全局可访问变量的影响。
答案 0 :(得分:94)
Javascript被定义为 reentrant 语言,这意味着没有向用户公开线程,实现中可能存在线程。像setTimeout()
和异步回调这样的函数需要等待脚本引擎在它们能够运行之前进入休眠状态。
这意味着事件中发生的所有事情必须在处理下一个事件之前完成。
话虽这么说,如果你的代码做了某些事情,你可能需要一个互斥锁,它希望在异步事件被触发和调用回调之间不会改变值。
例如,如果你有一个数据结构,其中点击一个按钮,并把它发送它调用的回调以破坏性方式的变化的数据结构的的XmlHttpRequest,你有另一个按钮直接改变相同的数据结构,当之间事件被触发,当执行回调时,用户可以在回调之前单击并更新数据结构,然后可能会丢失该值。
虽然您可以创建一个竞争条件,但是在代码中很容易阻止它,因为每个函数都是原子的。这将是一项很多工作,并采取一些奇怪的编码模式来实际创造竞争条件。
答案 1 :(得分:18)
这个问题的答案虽然在给出时是正确的,但有点过时了。如果查看不使用webworkers的客户端javascript应用程序,仍然是正确的。
关于网络工作者的文章:
multithreading in javascript using webworkers
Mozilla on webworkers
这清楚地表明,通过网络工作者的javascript具有多线程功能。关于问题是关于javascript需要的互斥量?我不确定这一点。但这个stackoverflow帖子似乎相关:
Mutual Exclusion for N Asynchronous Threads
答案 2 :(得分:8)
正如@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中的互斥,但据我所知,这是一个简单的互斥量,它很方便。
答案 3 :(得分:5)
JavaScript是单线程的......虽然Chrome可能是一个新的野兽(我认为它也是单线程的,但每个标签都有自己的JavaScript线程......我没有仔细研究它,所以不要'引用我的话。)
但是,您需要担心的一件事是您的JavaScript将如何处理多个ajax请求,而这些请求的返回顺序与您发送的顺序不同。所以,你真正需要担心的是确保你的ajax调用处理的方式是,如果结果以不同于你发送的顺序返回,它们将不会踩到彼此的脚。
这也适用于超时......
当JavaScript增长多线程时,可能会担心互斥体等......
答案 4 :(得分:2)
是的,在访问标签/窗口之间共享的资源时,Javascript中可能需要使用互斥锁,例如 localStorage 。
例如,如果用户打开了两个选项卡,则以下简单代码是不安全的:
function appendToList(item) {
var list = localStorage["myKey"];
if (list) {
list += "," + item;
}
else {
list = item;
}
localStorage["myKey"] = list;
}
在localStorage项目“已获取”和“设置”之间,另一个选项卡可能已修改该值。这通常不太可能,但可能 - 您需要自己判断在特定情况下与任何争用相关的可能性和风险。
有关更多详细信息,请参阅以下文章:
答案 5 :(得分:1)
事件已发出信号,但JavaScript执行仍然是单线程的。
我的理解是,当事件发出信号时,引擎会停止正在执行的操作以运行事件处理程序。处理程序完成后,将恢复脚本执行。如果事件处理程序更改了某些共享变量,则恢复的代码将看到这些更改“突然出现”。
如果你想“保护”共享数据,简单的布尔标志就足够了。
答案 6 :(得分:1)
JavaScript,语言,可以像你想要的那样多线程,但是javascript引擎的浏览器嵌入只运行一个回调(onload,onfocus,< script>等等),一个时间(大概是每个标签)。威廉建议使用Mutex进行注册和接收回调之间的更改不应该因为这个问题而过于字面化,因为您不希望阻止干预回调,因为解锁它的回调将在当前回调后被阻止! (哇,英语很糟糕,谈论线程。)在这种情况下,如果设置了一个标志,或者像setTimeout()那样设置了一个标志,你可能想要按照重新分配当前事件的方式做一些事情。
如果你使用不同的JS嵌入,并且一次执行多个线程,它可能会更加冒险,但由于JS可以如此轻松地使用回调并锁定对象的属性访问显式锁定不是几乎是必要的。但是,如果为使用多线程的通用代码(例如,游戏脚本)设计的嵌入也没有给出一些显式的锁定原语,我会感到惊讶。
对不起文字之墙!