为什么JavaScript不支持多线程?

331

这是故意设计的决定还是我们当前的浏览器存在问题,将在未来版本中纠正?


3
请参考JavaScript和线程问题的答案,了解有关Web Worker/Worker线程的信息。 - Sam Hasler
149
你好,Google的朋友。你可能会注意到这里的所有内容似乎都很陈旧(请注意此问题是五年前提出的)。自那时以来,Web浏览器已经具备了一些功能,据我所知,多线程功能已经得到了更多或更少的实现。请看Web Workers:http://msdn.microsoft.com/en-us/hh549259.aspx - ArtOfWarfare
3
Multithread.js 使用 Web Workers 封装了 JavaScript,可轻松实现多线程。适用于所有新浏览器,包括 iOS Safari。 :) - kwh
1
可能是JavaScript和线程的重复问题。 - Matt
一个服务工作者是一个脚本,你的浏览器在后台运行它,独立于网页之外,为不需要网页或用户交互的功能打开了大门。 - Omar bakhsh
16个回答

230
JavaScript不支持多线程,因为浏览器中的JavaScript解释器是单线程的(据我所知)。即使Google Chrome也不会让单个网页的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/

99
我认为“从未实现”这个说法太狭隘了。我保证Web应用程序最终将能够真正地支持多线程(这只是合乎逻辑的,因为Web应用程序变得更加主流,硬件变得更加并行)。就我的看法而言,由于JavaScript是Web开发的事实标准语言,它最终将不得不支持多线程或被具备此功能的其他语言所取代。 - devios1
6
从语言上讲,“永远不可能”可能有点过于绝对了 :) 但我仍然认为,真正的多线程 JavaScript 的优势在可预见的未来内是不可行的 ;) - Kamiel Wanrooij
5
虽然我认为 Web Workers 通过进程模型比线程模型更具并发性。Web Workers 使用消息传递作为通信手段,这是多线程应用程序中“常规”并发问题的优雅解决方案。但我不确定它们是否可以与主页面同时操作相同的对象。据我所知,它们无法访问 DOM。尽管大部分都是语义学上的问题,但从所有意图来看,Web Workers 看起来很有前途。 - Kamiel Wanrooij
1
困难远比额外的可能性要大得多。我不确定您是否考虑到了所有额外的可能性。我特别考虑到像游戏或图形可视化中使用webgl的情况。例如,考虑Google Maps的新3D版本。在许多3D模型的城市中,当需要渲染许多房屋时,我的电脑需要约2分钟才能加载所有内容。在某些角度上,我的显卡和网络都没有充分发挥作用。但是8个处理器中的1个处于100%。多线程也是fps方面的一个重要问题,就像这个例子所示:youtube.com/watch?v=sJ2p982cZFc - Scindix

32

11
这不更像是多进程的方法,而不是多线程的吗?线程被知道在单个堆内工作。 - beefeather
2
@beefeather,没错。这更像是一种过程方法。 - Neil
1
WebWorkers没有共享内存。为了彼此通信,它们发送消息。消息是内存的副本。这比共享内存慢。 - neoexpert
@neoexpert 这就是为什么在 JavaScript 中使用 SharedBufferArray,因为它们从同一内存块中读取。 - user3112634

25

传统上,JS仅用于短小、快速运行的代码。如果涉及重要计算,则在服务器上进行操作 - 在浏览器中运行长时间且执行较复杂任务的JS + HTML应用程序是荒谬的想法。

当然,现在我们已经有了这种应用程序。但是,需要花费一些时间让浏览器跟上步伐 - 大多数浏览器都是围绕单线程模型设计的,并且更改它并不容易。Google Gears 通过要求将后台执行隔离来避免许多潜在问题 - 不更改DOM(因为它不支持多线程),不访问主线程创建的对象(同样如此)。虽然有限制,但这很可能是最实用的设计,在不久的将来,这简化了浏览器的设计,也减少了允许缺乏经验的JS编程人员玩弄线程所涉及的风险...

@marcio:

那么,这不是实现JavaScript多线程的原因吗?程序员可以使用他们拥有的工具做任何他们想做的事情。

那么,让我们不要给他们太容易“滥用”的工具,以至于我打开的每个网站都会崩溃我的浏览器。这样的天真实现将使您直接进入导致MS在IE7开发过程中遇到诸多问题的领域:插件作者对线程模型进行了快速和松散的处理,导致隐藏的错误在对象生命周期发生变化时变得明显。这是坏事。如果您正在为IE编写多线程ActiveX插件,则认为它是随之而来的;这并不意味着它需要走得更远。


7
“它减少了允许经验不足的JS程序员搞乱线程所涉及到的风险。”为什么这是不实现JavaScript多线程的理由?程序员可以根据他们拥有的工具做任何想做的事情,好坏由他们自己承担。而且由于Google Chrome进程模型的存在,即使出错也不会影响其他应用程序。 :) - Marcio Aguiar
5
“让我们不要给程序员太容易被滥用的工具,否则我打开的每个网站都会导致浏览器崩溃。” - 什么?按照同样的逻辑,任何一种语言都不应该有多线程,因为如果有了多线程,你尝试打开的每个程序都会崩溃。但实际上并非如此。大多数语言都有线程,而大多数初学者不会使用它,那些使用了它的人中,绝大多数也不会将其投入生产,这些应用也不会变得流行或广泛使用。 - ArtOfWarfare

13

我不知道这个决定的理由,但是我知道你可以使用setTimeout模拟一些多线程编程的好处。你可以产生多个进程同时做事的错觉,尽管在现实中,所有的操作都发生在一个线程中。

只需要让你的函数做一点工作,然后调用类似下面的内容:

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

当有机会时,会执行任何其他需要完成的任务(例如UI更新、动画图像等)。


大多数情况下,我想在setTimeout内部使用一个loop,但显然这样做不起作用。你有没有做过类似的事情或者有什么技巧?例如,对于一个包含1000个元素的数组,我希望在两个setTimeout调用中使用两个for循环,第一个循环遍历并打印元素0..499,第二个循环遍历并打印元素500..999 - benjaminz
通常的技巧是保存状态并继续执行。例如,假设您想要打印0到1000,您可以先打印0到499,然后使用setTimeout技巧和参数500。代码内部将知道如何从该参数(500)开始循环。 - Eyal

13

你是指为什么语言本身不支持多线程还是为什么浏览器中的JavaScript引擎不支持多线程?

第一个问题的答案是,在浏览器中运行的JavaScript被设计成在沙盒环境中以及与机器/操作系统无关的方式运行,添加多线程支持会使语言变得复杂,并将语言与操作系统联系得太紧密。


12

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

因此,规则是:

  • 如果需要执行I/O绑定操作,则使用内部机制(即回调/承诺/async-await)
  • 如果需要执行CPU绑定操作,则使用工作线程。

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

否则,如果需要使用匿名函数执行大量CPU负载,则可以使用https://github.com/wilk/microjob,这是围绕工作线程构建的微小库。


6

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

总的来说,多线程非常非常非常困难(我说过它很难吗?),除非你加入额外的限制(比如仅使用不可变数据)。


3

3
英特尔一直在进行关于JavaScript多线程的开源研究,最近在2012年GDC上展示了成果。以下是视频链接:video。该研究小组使用了主要针对英特尔芯片组和Windows操作系统的OpenCL。该项目被命名为RiverTrail,并且代码可在GitHub上获取。
以下是更多有用的链接: Building a Computing Highway for Web Applications

2

Javascript是一种单线程语言。这意味着它只有一个调用堆栈和一个内存堆。正如预期的那样,它按顺序执行代码,并在移动到下一个代码片段之前必须完成执行。它是同步的,但有时可能会有害。例如,如果一个函数需要花费很长时间才能执行或者必须等待某些东西,那么在此期间它会冻结所有操作。


这并没有回答“为什么”JavaScript是这样的,只是解释了单线程的原因。 - JonHerbert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接