既然JavaScript是单线程的,那么HTML5中的web工作者是如何进行多线程的呢

Since JavaScript is single-threaded, how are web workers in HTML5 doing multi-threading?

本文关键字:工作者 何进行 多线程 web HTML5 单线程 JavaScript 那么 既然 中的      更新时间:2023-09-26

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

那么网络工作者是如何进行多线程工作的呢?或者,如果不是真正的多线程,他们是如何模拟它的?

正如一些评论已经指出的,Workers确实是多线程的。

一些要点可能有助于澄清你的想法:

  • JavaScript是一种语言,它不定义线程模型,也不一定是单线程的
  • 历史上,大多数浏览器都是单线程的(尽管这种情况正在迅速变化:IE、Chrome、Firefox),大多数JavaScript实现都发生在浏览器中
  • Web Workers不是JavaScript的一部分,它们是可以通过JavaScript访问的浏览器功能

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

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

实际上,不需要查看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世界。

请求的执行和回调的异步调用实际上是通过使用事件循环实现的,而不是多线程。以下是一些参考,在某些情况下有点过时,但我想现在仍然保留了主要思想。

  • Firefox:并发模型和事件循环-JavaScript|MDN
  • Chrome在某些操作系统中使用libevent
  • IE:了解事件模型(Internet Explorer)

这就是为什么JavaScript被认为是事件驱动而不是多线程的原因
请注意,JavaScript因此允许异步习惯用法,但不允许JavaScript代码的并行执行(在网络工作者之外)。术语异步只是表示两个指令的结果可能按加扰顺序处理。

至于WebWorkers,它们是JavaScript API,使开发人员能够控制多线程进程
因此,它们提供了明确的方式来处理对共享内存的并发访问(在不同线程中读取和写入值),这是通过以下方式实现的:

  • 您可以通过结构化克隆将数据推送给web工作者(这意味着新线程读取数据):结构化克隆算法-web API|MDN。本质上没有"共享"变量,而是为新线程提供对象的新副本
  • 您可以通过转移值的所有权将数据推送给web工作者:Transferable-WebAPI|MDN。这意味着只有一个线程可以在任何时候读取其值
  • 至于web工作者返回的结果(他们是如何"写"的),主线程在被提示时访问结果(例如使用指令thisWorker.onmessage = function(e) {console.log('Message ' + e.data + ' received from worker');})。我想,它一定是通过通常的事件循环实现的
  • 主线程和web工作程序访问一个真正共享的内存SharedArrayBuffer,该内存使用Atomic函数进行线程安全访问。我在这篇文章中发现了这一点:JavaScript:From Workers to Shared Memory
  • 注意:网络工作者无法访问DOM,这是真正共享的

您将一个.js文件生成为一个"worker",它在一个单独的线程中运行进程。您可以在JSON数据和"主"线程之间来回传递JSON数据。不过,工作人员不能访问某些东西,比如DOM。

因此,如果你想解决复杂的数学问题,你可以让用户在浏览器中输入内容,将这些变量传递给工作人员,让它在后台进行计算,而在主线程中,你让用户做其他事情,或者显示进度条或其他什么,然后当工作人员完成后,它将答案传递回来,然后打印到页面上。您甚至可以异步处理多个问题,并在问题完成时无序地返回答案。非常整洁!

实际上,我认为这里的主要困惑在于,人们正在寻找同时做事的聪明方法。如果你仔细想想,JavaScript显然不是多线程的,但我们有并行的方法,那么发生了什么?

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

浏览器启动一个带有要执行的javascript的线程。所以这是一个真正的线程,有了这个网络工作者的东西,你的js就不再是单线程的了。

声称";JavaScript是一种语言,它不定义线程模型,也不一定是单线程的;是从介质文章中直接复制粘贴的。。。它混淆了,却没有解决疑问。

它不一定像所有其他语言一样是单线程的。是的。。。但是

Javascript是一种用于单线程编程的语言,这就是它的美妙之处,使它易于实现。它是围绕单个调用堆栈设计的。也许在未来,随着新的实现,它将成为一种多线程编程语言。。。但是现在,啊。

Node V8仍然是单线程的,但它通过在用C++编写的LIBUV上创建工作线程来实现多线程功能。

同样,即使Javascript不适用于多线程,您也可以通过使用浏览器API来实现有限的多线程。

每次在浏览器上打开TAB时,它都会创建一个新线程,这个过程与web工作者相同。

它在内部工作,但不能访问任何窗口对象。

是的,人们可以称之为多线程,如果它让他们快乐,但在2021年,答案是"JS是指单线程编程(或单线程语言),但有限的多线程可以通过使用浏览器API(如"Web Workers")来实现;

众所周知,JavaScript是单线程的:所有代码都按顺序排队执行。

使用Web Workers,我们可以并发地运行JavaScript进程(或者至少在该语言允许的情况下接近并发地运行)。这种方法的主要好处是在不干扰用户界面的情况下处理后台线程中的数据操作。

使用web worker:

Web Workers允许您在网页上并行运行JavaScript,而不会阻塞用户界面。

  • Web工作程序在单独的线程中执行
  • 需要将所有工作程序代码托管在单独的文件中
  • 它们不是自动垃圾收集的,所以你需要控制它们
  • 运行工作人员使用worker.postMessage(")
  • 阻止工人有两种方法terminate()来自调用方代码和关闭()来自Worker本身
  • 实例化一个工作者将花费一些内存

Web Worker在一个独立的线程中运行。因此,它们执行的代码需要包含在一个单独的文件中。但在我们做到这一点之前,首先要做的是创造一个新的工人对象。构造函数采用工作脚本的名称:

var worker = new Worker('task.js'); 

如果指定的文件存在,浏览器将生成一个新的工作线程,该线程将异步下载。在文件完全下载并执行之前,工作程序不会开始。如果指向工作进程的路径返回404,则该工作进程将以静默方式失败。

创建工作者后,通过调用postMessage()方法:

worker.postMessage(); // Start the worker. 

通过消息传递与工作人员通信

工作与其父页之间的通信是使用事件模型和postMessage()方法根据您的浏览器/版本,postMessage()可以接受字符串或JSON对象作为其单个参数。最新版本的现代浏览器支持传递JSON对象。

下面是一个在doWork.js中使用字符串将"Hello World"传递给工作者的示例。工作者只需返回传递给它的消息。

主脚本:

var worker = new Worker('doWork.js'); 
worker.addEventListener('message', function(e) { 
  console.log('Worker said: ', e.data); 
}, false); 
 
worker.postMessage('Hello World'); // Send data to our worker.

doWork.js(工人):

self.addEventListener('message', function(e) { 
  self.postMessage(e.data); // Send data back to main script 
}, false); 

何时postMessage()从主页面调用,我们的工作人员通过定义onmessage的处理程序消息事件消息负载(在本例中为"Hello World")可在中访问Event.data。此示例演示postMessage()也是将数据传递回主线程的方法。实用的

参考文献:

http://www.tothenew.com/blog/multi-threading-in-javascript-using-web-workers/

https://www.html5rocks.com/en/tutorials/workers/basics/

https://dzone.com/articles/easily-parallelize-jobs-using-0