为啥 JavaScript 不支持多线程?

Posted

技术标签:

【中文标题】为啥 JavaScript 不支持多线程?【英文标题】:Why doesn't JavaScript support multithreading?为什么 JavaScript 不支持多线程? 【发布时间】:2010-09-07 13:56:31 【问题描述】:

这是一个深思熟虑的设计决定还是我们当前浏览器存在的问题,这些问题将在未来的版本中得到纠正?

【问题讨论】:

另请参阅javascript and Threads 问题的答案,了解有关网络工作者/工作者线程的信息。 您好,Google 员工。您可能会注意到这里的所有内容似乎都过时了(请注意,这个问题是 5 年前提出的。)自从被问到,Web 浏览器已经获得了一些功能,据我所知,这些功能或多或少是多线程的。看看 Web Workers:msdn.microsoft.com/en-us/hh549259.aspx Multithread.js 包装了 Web Workers 并允许在 JS 中轻松实现多线程。适用于所有新浏览器,包括 ios Safari。 :) JavaScript and Threads的可能重复 您可以使用 (server worker) - 服务工作者是您的浏览器在后台运行的脚本,与网页分开,为不需要网页的功能打开大门或用户交互。 【参考方案1】:

据我所知,谷歌浏览器会有多线程 javascript,所以这是一个“当前实现”的问题。

【讨论】:

【参考方案2】:

你的意思是为什么语言不支持多线程,或者浏览器中的 JavaScript 引擎为什么不支持多线程?

第一个问题的答案是,浏览器中的 JavaScript 旨在以独立于机器/操作系统的方式在沙盒中运行,添加多线程支持会使语言复杂化并使语言与操作系统过于紧密地联系在一起.

【讨论】:

【参考方案3】:

这是不支持多线程的实现。目前,Google Gears 提供了一种通过执行外部进程来使用某种形式的并发性的方法,仅此而已。

谷歌今天应该发布的新浏览器(谷歌浏览器)通过在进程中分离来并行执行一些代码。

核心语言当然可以提供与 Java 相同的支持,但对 Erlang 的并发性之类的支持还很遥远。

【讨论】:

【参考方案4】:

我不知道这个决定的基本原理,但我知道您可以使用 setTimeout 模拟多线程编程的一些好处。您可以产生多个进程同时执行操作的错觉,但实际上,所有事情都发生在一个线程中。

让你的函数做一点工作,然后调用类似的东西:

setTimeout(function () 
    ... do the rest of the work...
, 0);

任何其他需要做的事情(如 UI 更新、动画图像等)都会在他们有机会时发生。

【讨论】:

大多数情况下,我想在setTimeout 中使用loop,但显然这不起作用。你有没有做过类似的事情或者你有黑客?一个例子是 1000 个元素的数组,我希望在两个 setTimeout 调用中使用两个 for 循环,这样第一个循环遍历并打印元素 0..499,第二个循环遍历并打印元素 500..999 通常技术是保存状态并继续。例如,假设您要打印 0 到 1000,您可以打印 0 到 499,然后使用参数 500 执行 setTimeout 技巧。里面的代码会知道获取参数 (500) 并从那里开始循环。跨度> 【参考方案5】:

传统上,JS 旨在编写简短、快速运行的代码。如果您要进行主要计算,您可以在服务器上进行 - 在浏览器中长时间运行的 JS+html app 的想法是荒谬的。 p>

当然,现在我们有了。但是,浏览器需要一点时间才能赶上——它们中的大多数都是围绕单线程模型设计的,改变这一点并不容易。 Google Gears 通过要求隔离后台执行来回避许多潜在问题 - 不更改 DOM(因为这不是线程安全的),不访问由主线程创建的对象(同上)。虽然具有限制性,但这可能是在不久的将来最实用的设计,既因为它简化了浏览器的设计,又因为它降低了让没有经验的 JS 编码人员乱用线程所涉及的风险......

@marcio:

为什么不使用 Javascript 实现多线程?程序员可以使用他们拥有的工具为所欲为。

那么,我们不要给他们提供容易误用的工具,以至于我打开的每个其他网站都会导致我的浏览器崩溃。一个幼稚的实现会让你直接进入在 IE7 开发过程中给 MS 带来很多麻烦的领域:附加组件作者在线程模型上玩得又快又松,导致隐藏的错误在主线程上的对象生命周期发生变化时变得明显.坏的。如果您正在为 IE 编写多线程 ActiveX 附加组件,我猜它是随领域而来的;并不意味着它需要走得更远。

【讨论】:

"它降低了所涉及的风险 > 允许没有经验的 JS 编码人员 > 乱用线程" 为什么这是不在 Javascript 中实现多线程的原因?程序员可以使用他们拥有的工具做任何他们想做的事情。不管是好是坏,都是他们的问题。使用 Google Chrome 进程模型,它甚至不会影响其他应用程序。 :) @Shog9 - “我们不要为 [程序员] 提供容易被滥用的工具,以至于我打开的每个其他网站都会导致浏览器崩溃。” - 什么?按照同样的逻辑,任何语言都不应该有多线程,因为如果他们提供你尝试打开的所有其他程序都会崩溃。除非它不是那样工作的。线程在大多数语言中都存在,大多数新手程序员都不会接触它,大多数不会将其投入生产,以及那些从未流行或广泛使用的应用程序。【参考方案6】:

正如matt b所说,问题不是很清楚。假设您要询问语言中的多线程支持:因为当前在浏览器中运行的 99.999% 的应用程序不需要它。如果你真的需要它,有一些变通方法(比如使用 window.setTimeout)。

一般来说,多线程非常、非常、非常、非常、非常非常困难(我说这很难吗?),除非你施加额外的限制(比如只使用不可变数据)。

【讨论】:

【参考方案7】:

JavaScript 不支持多线程,因为浏览器中的 JavaScript 解释器是单线程 (AFAIK)。即使是谷歌浏览器也不会让单个网页的 JavaScript 并发运行,因为这会导致现有网页出现大量并发问题。 Chrome 所做的只是将多个组件(不同的选项卡、插件等)分离到不同的进程中,但我无法想象一个页面有多个 JavaScript 线程。

但是,您可以按照建议使用setTimeout 来允许某种调度和“假”并发。这会导致浏览器重新获得对渲染线程的控制权,并在给定的毫秒数后启动提供给setTimeout 的 JavaScript 代码。如果您希望在对其执行操作时允许刷新视口(您所看到的),这将非常有用。只是循环通过例如坐标和相应地更新一个元素只会让你看到开始和结束位置,中间什么都没有。

我们在 JavaScript 中使用了一个抽象库,它允许我们创建由同一个 JavaScript 解释器管理的进程和线程。这允许我们以以下方式运行操作:

进程 A,线程 1 进程 A,线程 2 进程 B,线程 1 进程 A,线程 3 进程 A,线程 4 进程 B,线程 2 暂停进程 A 进程 B,线程 3 进程 B,线程 4 进程 B,线程 5 启动进程 A 进程 A,线程 5

这允许某种形式的调度和伪造并行、线程的启动和停止等,但它不是真正的多线程。我认为它永远不会在语言本身中实现,因为真正的多线程只有在浏览器可以运行单页多线程(甚至多个核心)时才有用,而且困难更大比额外的可能性。

关于 JavaScript 的未来,请查看以下内容: https://developer.mozilla.org/presentations/xtech2006/javascript/

【讨论】:

我认为 从未实施 的愿景过于狭隘。我保证 Web 应用程序最终将能够成为真正的多线程(这是合乎逻辑的,因为 Web 应用程序变得越来越占主导地位,硬件变得更加并行),并且在我看来,由于 JavaScript 是 Web 开发的事实语言,它最终将不得不支持多线程或被某些支持的东西所取代。 Never 可能有点过于大胆了 :) 但我仍然认为真正的多线程 javascript 的优势在可预见的未来是不可行的 ;) 虽然我认为 web worker 通过进程模型比线程模型更具有并发性。 Web 工作者使用消息传递作为通信手段,这是多线程应用程序中“常见”并发问题的优雅解决方案。我不确定他们是否可以同时对与主页相同的对象进行操作。据我所知,他们无法访问 DOM。虽然其中大部分是语义,但网络工作者看起来对所有意图和目的都有希望。 困难远大于额外的可能性 我不确定你是否想到了所有额外的可能性。我特别想到在游戏或图形可视化中使用 webgl 的情况。例如。考虑新的 3D 版本的谷歌地图。在有许多 3D 模型的城市中,当必须渲染许多房屋时,我的 PC 需要大约 2 分钟来加载所有内容。在某些角度,我的显卡和网络都无法正常工作。但是 8 个处理器中有 1 个是 100%。就 fps 而言,多线程也是一个大问题,如下例所示:youtube.com/watch?v=sJ2p982cZFc【参考方案8】:

如果没有对线程同步的适当语言支持,尝试新的实现甚至没有意义。现有的复杂 JS 应用程序(例如任何使用 ExtJS 的应用程序)很可能会意外崩溃,但如果没有 synchronized 关键字或类似的东西,编写行为正确的新程序也将非常困难甚至不可能。

【讨论】:

【参考方案9】:

JavaScript 多线程(有一些限制)就在这里。 Google 为 Gears 实施了工作程序,并且 HTML5 中包含了工作程序。大多数浏览器已经添加了对此功能的支持。

数据的线程安全得到保证,因为与工作人员通信的所有数据都经过序列化/复制。

欲了解更多信息,请阅读:

http://www.whatwg.org/specs/web-workers/current-work/

http://ejohn.org/blog/web-workers/

【讨论】:

但这不是更多的多进程方法而不是多线程吗?众所周知,线程在单个堆中工作。 @befeather,这是真的。它更像是一种过程方法。 WebWorkers 没有共享内存。为了相互交流,他们发送消息。消息是内存的副本。这比共享内存要慢。【参考方案10】:

英特尔一直在对 Javascript 中的多线程进行一些开源研究,最近在 GDC 2012 上进行了展示。 这是video 的链接。该研究小组使用了主要关注英特尔芯片组和 Windows 操作系统的 OpenCL。该项目代号为 RiverTrail,代码在 GitHub 上提供

一些更有用的链接:

Building a Computing Highway for Web Applications

【讨论】:

【参考方案11】:

目前一些浏览器确实支持多线程。因此,如果您需要,您可以使用特定的库。例如,查看下一个材料:

https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers (支持后台线程);

https://keithwhor.github.io/multithread.js/(图书馆)。

【讨论】:

【参考方案12】:

使用 HTML5 带来的 webworkers 显然可以使用 javascript 进行多线程。

webworkers 和标准多线程环境之间的主要区别是内存资源不与主线程共享,对对象的引用从一个线程到另一个线程不可见。线程通过交换消息进行通信,因此可以按照事件驱动的设计模式实现同步和并发方法调用算法。

存在许多允许在线程之间构建编程的框架,其中包括 OODK-JS,一种支持并发编程的 OOP js 框架 https://github.com/GOMServices/oodk-js-oop-for-js

【讨论】:

共享内存是线程的确切定义,与单独的进程相对(例如 fork() vs exec())。线程可以共享对象,进程必须使用IPC。 Web Worker 不是多线程的。【参考方案13】:

但是,您可以使用 eval 函数将并发性提高到一定程度

/* content of the threads to be run */
var threads = [
        [
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');",
            "document.write('Foo <br/>');"
        ],
        [
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');",
            "document.write('Bar <br/>');"
        ]
    ];

window.onload = function() 
    var lines = 0, quantum = 3, max = 0;

    /* get the longer thread length */
    for(var i=0; i<threads.length; i++) 
        if(max < threads[i].length) 
            max = threads[i].length;
        
    

    /* execute them */
    while(lines < max) 
        for(var i=0; i<threads.length; i++) 
            for(var j = lines; j < threads[i].length && j < (lines + quantum); j++) 
                eval(threads[i][j]);
            
        
        lines += quantum;
    

【讨论】:

【参考方案14】:

Node.js 10.5+ 支持 工作线程 作为实验性功能(您可以在启用 --experimental-worker 标志的情况下使用它):https://nodejs.org/api/worker_threads.html

所以,规则是:

如果您需要执行I/O 绑定操作,请使用内部机制(也称为回调/承诺/异步等待) 如果您需要执行 CPU 绑定操作,请使用工作线程。

工作线程旨在成为长期存在的线程,这意味着您会生成一个后台线程,然后通过消息传递与其进行通信。

否则,如果您需要使用匿名函数执行繁重的 CPU 负载,那么您可以使用 https://github.com/wilk/microjob,这是一个围绕工作线程构建的小型库。

【讨论】:

【参考方案15】:

Javascript 是一种单线程语言。这意味着它有一个调用栈和一个内存堆。正如预期的那样,它按顺序执行代码,并且必须在执行下一段代码之前完成一段代码。它是同步的,但有时可能是有害的。例如,如果一个函数需要一段时间才能执行或必须等待某事,它会同时冻结所有内容。

【讨论】:

【参考方案16】:

其实多线程与语言本身无关。 这是一个用于 .NET 的多线程 Javascript 引擎。 它适用于多线程场景。 它与 C# 运行时集成,因此所有同步逻辑都类似于 C#。您可以启动/等待/等待任务,也可以启动线程。你甚至可以放锁。以下示例演示了在 .NET 运行时中使用 Javascript 语法的并行循环。

https://github.com/koculu/topaz

var engine = new TopazEngine();
engine.AddType(typeof(Console), "Console");
topazEngine.AddType(typeof(Parallel), "Parallel");
engine.ExecuteScript(@"
var sharedVariable = 0
function f1(i) 
    sharedVariable = i

Parallel.For(0, 100000 , f1)
Console.WriteLine(`Final value: sharedVariable`);
");

除此之外,Microsoft 正在开发 Napa.js,这是一个支持多线程的 Node.js 克隆。

https://github.com/microsoft/napajs

【讨论】:

以上是关于为啥 JavaScript 不支持多线程?的主要内容,如果未能解决你的问题,请参考以下文章

python中的多线程为啥会报错?

为啥lua语言中使用全局变量就会造成内存泄漏

java多线程聊天,两个客户端通过服务端转发消息实现通信,可是为啥信息发不出去,?以下客户端和服务端

多线程执行,为啥明明已经加了synchronized了,还是出现每次结果都不一样呢?

为啥 JavaScript 在普通浏览器中没有自己的线程?

为啥局部变量在 Java 中是线程安全的