是否保证JavaScript是单线程的?

2020/09/23 06:01 · javascript ·  · 0评论

众所周知,JavaScript在所有现代浏览器实现中都是单线程的,但是它是在任何标准中指定的,还是仅是传统的?假定JavaScript始终是单线程的,是否完全安全?

这是个好问题。我很想说“是”。我不能

通常认为JavaScript具有脚本(*)可见的单个执行线程,因此,当您输入内联脚本,事件侦听器或超时时,您将完全处于控制状态,直到从块或函数的结尾返回为止。

(*:忽略浏览器是否真的使用一个OS线程来实现其JS引擎,或者WebWorkers是否引入了其他有限的执行线程的问题。)

但是,实际上这并不是真的,以偷偷摸摸的方式令人讨厌。

最常见的情况是即时事件。当您的代码执行某些操作导致它们时,浏览器会立即将其触发:

var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
    l.value+= 'blur\n';
};
setTimeout(function() {
    l.value+= 'log in\n';
    l.focus();
    l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">

log in, blur, log out除IE之外的所有结果上。这些事件不仅会因为您focus()直接调用而发生,还可能是因为您调用alert(),打开了一个弹出窗口或其他任何会移动焦点的事件而发生。

这也可能导致其他事件。例如,添加一个i.onchange侦听器,然后在focus()调用取消焦点之前在输入中键入某些内容,并且日志顺序为log in, change, blur, log out,除了Opera所在的位置log in, blur, log out, change和IE所在的位置(甚至更少)log in, change, log out, blur

同样,调用click()提供它的元素会onclick在所有浏览器中立即调用处理程序(至少这是一致的!)。

(我在这里使用直接on...事件处理程序属性,但addEventListenerand 也会发生同样的情况attachEvent。)

在很多情况下,尽管您没有做任何事情来激发代码,但在线程插入代码的同时事件仍可能触发一个例子:

var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
    l.value+= 'alert in\n';
    alert('alert!');
    l.value+= 'alert out\n';
};
window.onresize= function() {
    l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>

点击alert,您将看到一个模态对话框。在关闭该对话框之前不会执行任何脚本,是吗?不。调整主窗口的大小,您将进入alert in, resize, alert out文本区域。

您可能会认为在模式对话框打开时无法调整窗口的大小,但事实并非如此:在Linux中,您可以随意调整窗口的大小。在Windows上并不是那么容易,但是您可以通过将屏幕分辨率从不适合窗口的较大屏幕分辨率更改为较小屏幕分辨率来调整屏幕分辨率来实现。

您可能会认为,当用户由于脚本是线程而无法与浏览器进行有效交互时,只有resize(可能还有更多类似scroll)会触发。对于单个窗口,您可能是正确的。但是,一旦您执行跨窗口脚本编写,这些便全部投入使用。对于Safari以外的所有浏览器,当它们中的任何一个忙时都会阻止所有窗口/选项卡/框架时,您可以与另一个文档的代码中的文档进行交互,在单独的执行线程中运行,并使所有相关的事件处理程序火。

在脚本仍然线程化的情况下,可以引发可能引起事件发生的地方:

  • 当模式弹出窗口(alertconfirmprompt)是开放的,在所有的浏览器,但歌剧;

  • showModalDialog支持它的浏览器中;

  • 即使您选择让脚本继续运行,“此页面上的脚本可能正忙于...”对话框也会允许调整大小和模糊等事件触发并处理,即使该脚本位于脚本中间。忙循环,Opera除外。

  • 对我而言,前一段时间,在带有Sun Java Plugin的IE中,调用applet上的任何方法都可以触发事件并重新输入脚本。自始至终,这始终是一个对时间敏感的错误,Sun可能已经修复了此错误(我当然希望如此)。

  • 可能更多。自从我测试了这已经有一段时间了,浏览器也因此变得越来越复杂。

In summary, JavaScript appears to most users, most of the time, to have a strict event-driven single thread of execution. In reality, it has no such thing. It is not clear how much of this is simply a bug and how much deliberate design, but if you're writing complex applications, especially cross-window/frame-scripting ones, there is every chance it could bite you — and in intermittent, hard-to-debug ways.

If the worst comes to the worst, you can solve concurrency problems by indirecting all event responses. When an event comes in, drop it in a queue and deal with the queue in order later, in a setInterval function. If you are writing a framework that you intend to be used by complex applications, doing this could be a good move. postMessage will also hopefully soothe the pain of cross-document scripting in the future.

我会说是-因为如果浏览器的javascript引擎异步运行它,则几乎所有现有(至少所有不重要的)javascript代码都会中断。

除此之外,HTML5已经指定了将Web多线程引入基本Javascript中的Web Workers(用于多线程javascript代码的显式,标准化API)这一事实几乎没有意义。

(Note to others commenters: Even though setTimeout/setInterval, HTTP-request onload events (XHR), and UI events (click, focus, etc.) provide a crude impression of multi-threadedness - they are still all executed along a single timeline - one at a time - so even if we don't know their execution order beforehand, there's no need to worry about external conditions changing during the execution of an event handler, timed function or XHR callback.)

是的,尽管在使用任何异步API(例如setInterval和xmlhttp回调)时仍然会遇到一些并发编程问题(主要是竞争条件)。

是的,尽管Internet Explorer 9将在单独的线程上编译Javascript以准备在主线程上执行。但是,这对于您作为程序员来说并没有任何改变。

我要说的是,说明书中并没有防止有人从创建发动机运行 JavaScript的上多个线程,要求代码以访问共享对象状态执行同步。

我认为,单线程非阻塞范例是出于在ui永不阻塞的浏览器运行javascript的需要

Nodejs遵循了浏览器的方法

但是Rhino引擎支持在不同线程中运行js代码执行不能共享上下文,但是可以共享作用域。对于这种特定情况,文档说明:

...“ Rhino保证跨线程访问JavaScript对象的属性是原子的,但不再保证在同一范围内同时执行的脚本。如果两个脚本同时使用同一范围,则这些脚本是负责协调对共享变量的所有访问。”

通过阅读Rhino文档,我得出的结论是,有人可以编写也产生新的JavaScript线程的javascript API,但是该api是特定于Rhino的(例如,节点只能产生新的进程)。

我想即使对于支持JavaScript中多个线程的引擎,也应该与不考虑多线程或阻塞的脚本兼容。

Concearning 浏览器的NodeJS的方式,我看到它是:

    1. 是否所有js代码都在单个线程中执行是的。
    1. JS代码造成其他线程运行是的。
    1. 这些线程可以改变js执行上下文吗?:否。但是它们可以(直接/间接(?))追加到事件队列中,侦听器可以
      事件队列
      改变执行上下文但不要上当,监听器会再次在主线程上自动运行

因此,在使用浏览器和Node.js(可能还有许多其他引擎)的情况下,javascript不是多线程的,而引擎本身是


有关网络工作者的更新:

网络工作人员的存在进一步证明了javascript可以是多线程的,因为有人可以在javascript中创建将在单独的线程上运行的代码。

但是,网络工作者不会担心可以共享执行上下文的传统线程的问题上面的规则2和3仍然适用,但是这次线程代码是由用户(js代码编写器)在javascript中创建的。

效率而非并发)的角度来看,唯一要考虑的是产生的线程数见下文:

关于线程安全性

Worker接口产生真正的OS级线程,并且如果您不小心,那么谨慎的程序员可能会担心并发会在您的代码中引起“有趣的”结果。

但是,由于Web工作人员已经仔细控制了与其他线程的通信点,因此实际上很难引起并发问题无法访问非线程安全组件或DOM。而且,您必须通过序列化对象将特定数据传入和传出线程。因此,您必须非常努力地在代码中引起问题。


聚苯乙烯

除了理论外,还应始终准备好接受的答案中描述的可能的极端情况和错误

JavaScript / ECMAScript旨在驻留在主机环境中。也就是说,除非宿主环境决定解析并执行给定脚本,并提供使JavaScript实际上有用的环境对象(例如浏览器中的DOM),否则JavaScript实际上不会做任何事情

我认为给定的功能或脚本块将逐行执行,这对于JavaScript是有保证的。但是,主机环境可能可以同时执行多个脚本。或者,主机环境始终可以提供提供多线程的对象。setTimeoutsetInterval是一个例子,或主机环境的至少伪例子,提供一种方式做一些并发性(即使它不完全并发)。

实际上,父窗口可以与运行自己的执行线程的子窗口或同级窗口或框架进行通信。

@Bobince提供了一个非常模糊的答案。

摆脱了MárÖrlygsson的回答,由于以下简单事实,Javascript始终是单线程的:Javascript中的所有内容都沿着单个时间轴执行。

那是单线程编程语言的严格定义。

没有。

我要反对这里的人群,但要忍受我。单个JS脚本旨在有效地实现单线程,但这并不意味着不能以不同的方式解释它。

假设您有以下代码...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

编写此代码时,期望在循环结束时,列表必须具有10000个索引平方的条目,但是VM可能会注意到循环的每次迭代都不会影响其他迭代,并使用两个线程进行重新解释。

第一线程

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

第二线程

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

我在这里简化一下,因为JS数组更复杂,然后是笨拙的内存块,但是如果这两个脚本能够以线程安全的方式向数组添加条目,那么当两个脚本执行完毕时,结果与单线程版本相同。

虽然我不知道有任何VM会检测到这样的可并行化代码,但它似乎有可能在将来用于JIT VM,因为在某些情况下它可以提供更高的速度。

进一步扩展该概念,可以对代码进行注释,以使VM知道要转换为多线程代码的内容。

// like "use strict" this enables certain features on compatible VMs.
"use parallel";

var list = [];

// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

由于Web Workers正在使用Javascript,因此这种不太丑的系统不太可能出现,但是我认为可以肯定地说Javascript是单线程的。

嗯,Chrome是多进程的,我认为每个进程都处理自己的Javascript代码,但据代码所知,它是“单线程”。

Javascript不支持多线程,至少没有明确支持,因此没有影响。

我尝试对@bobince的示例进行一些修改:

<html>
<head>
    <title>Test</title>
</head>
<body>
    <textarea id="log" rows="20" cols="40"></textarea>
    <br />
    <button id="act">Run</button>
    <script type="text/javascript">
        let l= document.getElementById('log');
        let b = document.getElementById('act');
        let s = 0;

        b.addEventListener('click', function() {
            l.value += 'click begin\n';

            s = 10;
            let s2 = s;

            alert('alert!');

            s = s + s2;

            l.value += 'click end\n';
            l.value += `result = ${s}, should be ${s2 + s2}\n`;
            l.value += '----------\n';
        });

        window.addEventListener('resize', function() {
            if (s === 10) {
                s = 5;
            }

            l.value+= 'resize\n';
        });
    </script>
</body>
</html>

因此,当您按Run(运行),关闭警报弹出窗口并执行“单线程”时,您应该看到类似以下内容:

click begin
click end
result = 20, should be 20

但是,如果您尝试在Windows上的Opera或Firefox稳定版中运行此程序,并在屏幕上弹出警告窗口的情况下最小化/最大化窗口,则将出现以下内容:

click begin
resize
click end
result = 15, should be 20

我不想说这是“多线程”,但是某些代码段在错误的时间内执行了,而我对此并不期望,现在我的状态已损坏。更好地了解这种行为。

尝试将两个setTimeout函数相互嵌套,它们将表现为多线程(即,外部计时器在执行其功能之前不会等待内部的定时器完成)。

本文地址:http://javascript.askforanswer.com/shifoubaozhengjavascriptshidanxianchengde.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!