由于JavaScript是单线程的,HTML5中的Web worker如何进行多线程处理?

时间:2012-03-14 18:41:15

标签: javascript html5 web-worker

我一直在阅读HTML5中的网络工作者,但我知道JavaScript是单线程的。

我的问题是:

Web工作人员如何进行多线程工作?或者如果它不是真正的多线程,它们如何模拟呢?在这里我似乎不太清楚。

5 个答案:

答案 0 :(得分:58)

正如一些评论已经指出的那样,工人真的是多线程的。

有些观点可能有助于澄清您的想法:

  • JavaScript是一种语言,它没有定义线程模型,它不一定是单线程的
  • 大多数浏览器历来都是单线程的(虽然这种情况正在迅速变化:IEChromeFirefox),大多数JavaScript实现都在浏览器中发生
  • Web Workers不是JavaScript的一部分,它们是可以通过JavaScript访问的浏览器功能

答案 1 :(得分:7)

有点晚了,但我问自己同样的问题,我想出了以下答案:
浏览器中的Javascript始终是单线程的,其根本后果是"并发"访问变量(多线程编程的主要问题)实际上并不是并发的;这是真的,除了webworkers ,它实际上是在单独的线程中运行,并且必须以某种明确的方式处理对变量的并发访问

我不是一个JavaScript忍者,但我也确信浏览器中的JavaScript是作为一个单线程进程提供的,没有过多关注它是否属实或者这个信念背后的理由。
支持这种假设的一个简单事实是,在使用JavaScript进行编程时,您不必关心对共享变量的并发访问。每个开发人员都没有考虑问题,而是编写代码,好像每次访问变量都是一致的 换句话说,您不必担心所谓的Memory model

实际上,没有必要让WebWorkers参与JavaScript中的并行处理。想想(异步)AJAX请求。并且想想你如何不小心处理对变量的并发访问:

var counter = 0;

function asyncAddCounter() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4) {
      counter++;
    }
  };
  xhttp.open("GET", "/a/remote/resource", true);
  xhttp.send();
}

asyncAddCounter();
counter++;

流程结束时counter的价值是多少?它是2。 它不会被读取和写入"同时"它永远不会产生1。这意味着对counter的访问始终是一致的。 如果两个线程同时真正访问该值,它们都可以通过阅读0开始,最后都写1

在浏览器中,开发人员隐藏了远程资源的实际数据获取,其内部工作超出了JavaScript API的范围(浏览器允许您根据JavaScript指令控制) 。就开发人员而言,网络请求的结果由主线程处理 简而言之,实际执行请求是不可见的,但是主线程执行回调(由自定义JavaScript代码处理结果)的调用。
可能,如果不是网络工作者,那么术语"多线程"永远不会进入Javascript世界。

执行请求和回调的异步调用实际上是通过使用事件循环而不是多线程来实现的。这对于几个浏览器来说都是如此,显然对于Node.js.以下是一些参考文献,在某些情况下有点过时,但我想现在仍然保留了主要的想法。

这就是为什么JavaScript被称为Event-driven而不是多线程的原因 请注意,JavaScript因此允许异步惯用语,但不允许并行执行JavaScript代码(Webworkers之外)。术语异步只表示两个指令的结果可能以加扰顺序处理的事实。

至于WebWorkers,它们是 JavaScript API,可让开发人员控制多线程进程。
因此,它们提供了处理共享内存的并发访问(在不同线程中读取和写入值)的显式方法,并且通过以下方式实现了这一点:

  • 您通过结构化克隆将数据推送到Web工作者(这意味着新线程读取数据):The structured clone algorithm - Web APIs | MDN。基本上没有"共享"变量,而是为新线程提供对象的新副本。
  • 您通过转移价值的所有权将数据推送到网络工作者:Transferable - Web APIs | MDN。这意味着只有一个线程可以随时读取其值。
  • 对于Web工作者返回的结果(他们如何"写"),主线程在提示时访问结果(例如使用指令thisWorker.onmessage = function(e) {console.log('Message ' + e.data + ' received from worker');})。它必须通过通常的事件循环,我必须假设。
  • 主线程和Web工作者访问真正的共享内存SharedArrayBuffer,使用Atomic函数进行线程安全访问。我在本文中明确地揭示了这一点:JavaScript: From Workers to Shared Memory
  • 注意:webworkers无法访问真正共享的DOM!

答案 2 :(得分:5)

您将.js文件生成为“worker”,并在单独的线程中运行进程。您可以在它和“主”线程之间来回传递JSON数据。但是,工作人员无法访问DOM等特定内容。

因此,如果您想要解决复杂的数学问题,您可以让用户将内容输入浏览器,将这些变量传递给工作人员,让它在后台进行计算,同时在主线程中用户执行其他操作,或显示进度条或其他内容,然后当工作人员完成后,它会将答案传回,然后将其打印到页面。您甚至可以异步执行多个问题,并在完成后不按顺序传回答案。非常整洁!

答案 3 :(得分:0)

浏览器使用您要执行的javascript启动一个线程。所以它是一个真正的线程,对于这个Web工作者来说,你的js不再是单线程的。

答案 4 :(得分:0)

实际上,我认为主要的困惑在于人们正在寻找同时做事的巧妙方法。如果你仔细想想 JavaScript 显然不是多线程的,但我们有办法并行做事,那到底是怎么回事?

提出正确的问题会带来答案。谁负责线程?上面有一个答案,说JS只是一种语言,而不是线程模型。完全正确!JavaScript 与它无关。责任落在 V8 身上。查看此链接了解更多信息 -> https://v8.dev/ 所以 V8 允许每个 JS 上下文有一个线程,这意味着,无论你多么努力,生成一个新线程都是不可能的。然而,人们产生了所谓的工人,我们感到困惑。为了回答这个问题,我问你以下问题。是否有可能启动 2 V8 和它们都来解释一些 JS 代码?正是我们问题的解决方案。工作人员通过消息进行通信,因为他们的上下文不同。它们是其他对我们的上下文一无所知的东西,因此它们需要一些以消息形式出现的信息。