1.什么是多线程
多线程是指在一个进程中同时执行多个线程的技术。线程是程序执行的基本单位,多个线程可以共享同一进程的资源(如内存、文件 等),并且可以并发执行,从而提高程序的效率和响应能力。
2.为什么用多线程
-
并发执行:多线程允许程序的多个部分同时运行,从而提高程序的执行效率,尤其是在多核处理器上。
-
资源共享:同一进程中的多个线程可以共享内存和资源,这使得线程之间的通信和数据交换比进程之间更高效。
-
响应性:在用户界面应用程序中,使用多线程可以使程序在执行长时间运行的任务时仍然保持响应,避免界面冻结。
-
任务分解:复杂的任务可以被分解成多个较小的子任务,并由不同的线程并行处理,从而加快整体处理速度。
-
提高性能:在某些情况下,特别是 I/O 密集型和计算密集型任务中,多线程可以显著提高程序的性能。
多线程编程也带来了复杂性,例如:
-
同步问题:多个线程同时访问共享资源时,可能会出现竞争条件,导致数据不一致。需要使用锁、信号量等机制来控制线程之间的访问。
-
死锁:当两个或多个线程相互等待对方释放资源时,可能导致程序无法继续执行。
-
调试困难:多线程程序的调试相对复杂,因为线程的执行顺序可能会影响程序的行为。
Web Worker
Web Workers 是一种在浏览器中运行 JavaScript 的机制,允许你在后台线程中执行代码,从而避免阻塞主线程(也就是用户界面线程)。这使得 Web Workers 特别适合处理耗时的计算任务或 I/O 操作,而不会影响用户体验。
1.原理
-
线程模型
-
主线程与 Worker 线程:Web Worker 在浏览器中创建一个独立的线程(Worker 线程),与主线程(UI 线程)分离。主线程负责处理用户界面和用户交互,而 Worker 线程用于执行耗时的计算或任务。
-
创建 Worker
-
Worker 实例化:通过
new Worker('worker.js')
创建一个 Worker 实例,worker.js
是包含要在 Worker 中执行的 JavaScript 代码的文件。Worker 代码在独立的全局上下文中运行。
-
事件驱动
-
消息传递:主线程与 Worker 之间通过消息传递进行通信。使用
postMessage()
方法将数据发送到 Worker,Worker 通过onmessage
事件接收消息,并可以通过postMessage()
将结果返回给主线程。
-
数据传递
-
克隆与传递:通过结构化克隆算法,主线程和 Worker 之间传递的数据会被克隆,而不是直接共享。这意味着 Worker 中的数据与主线程中的数据是独立的,避免了数据竞争和并发问题。
-
事件循环与调度
-
事件循环:Worker 线程也有自己的事件循环机制。当 Worker 执行完任务后,会将结果通过消息传递回主线程,主线程在空闲时会处理这些消息。
-
错误处理
-
错误捕获:Worker 中的错误不会直接影响主线程。可以通过
onerror
事件处理 Worker 中的错误,确保主线程能够正常运行。
-
限制与安全
-
安全性:Worker 不能直接访问 DOM、Window 对象或其他主线程的全局对象,确保了线程之间的隔离性和安全性。
主:
let work = new Worker('./worker.js')
work.onmessage = e => {
console.log(e.data)
}
worker.js:
function fb(n){
if(n==1 ||n==2){
return 1
}
return fb(n-1) + fb(n-2)
}
console.time('1')
let result=fb(43)
console.timeEnd('1')
self.postMessage('worker')
注释:可以通过console.time()和console.timeEnd()来计算运行时间,上面代码结果
2.特点和优势
-
独立运行:Web Worker在一个独立的线程中运行,与主线程互不干扰。这可以避免主线程因执行耗时操作而卡顿,从而提高页面的响应速度和用户体验。
-
隔离机制:Web Worker无法直接访问或操作DOM元素。这种隔离机制可以防止潜在的页面修改和恶意攻击,确保网页的安全性和稳定性。
-
多线程异步执行: Web Worker允许您在标签页进程中创建多个独立线程,每个线程都可以在不同的内核上执行,因此可以实现并行计算。
-
并行执行:Web Workers 允许你在独立的线程中执行 JavaScript 代码,这样可以实现并行处理,提高应用程序的性能。
-
非阻塞:由于 Worker 在后台线程中运行,主线程可以继续处理用户输入和其他任务,保持应用程序的响应性。
-
消息传递:Web Workers 通过消息传递进行通信。主线程和 Worker 之间不能直接共享变量,而是通过
postMessage
方法发送消息,并通过onmessage
事件接收消息。 -
没有 DOM 访问:Web Workers 不能直接访问 DOM,这样可以避免线程安全问题。它们只能通过消息与主线程进行交互。
-
错误处理:Web Workers 可以独立于主线程处理错误,使用
onerror
事件捕获 Worker 中的错误。
-
注意:
报错:Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///...' cannot be accessed from origin 'null'
表示 Web Worker 无法被加载,因为它的来源被认为是 null
。这是因为你是在本地文件系统(即使用 file://
协议)中打开 HTML 文件,而不是通过 HTTP/HTTPS 服务器。
分配给Worker 线程运行的脚本文件(worker.js),必须与主线程的脚本文件(main.js)同源。这里的同源限制包括协议、域名和端口,不支持本地地址(file://)。这会带来一个问题,我们经常使用CDN来存储js文件,主线程的worker.js的域名指的是html文件所在的域,通过new Worker(url)加载的url属于CDN的域,会带来跨域的问题,实际开发中我们不会吧所有的代码都放在一个文件中让子线程加载,肯定会选择模块化开发。通过工具或库把代码合并到一个文件中,然后把子线程的代码生成一个文件url。
file://
协议是用于访问本地文件系统的 URI(统一资源标识符)协议。当你在浏览器中打开一个本地文件(例如 HTML 文件)时,浏览器会使用 file://
协议来访问该文件。而Web Workers 不能从 file://
协议加载,因为这样做可能会引发安全风险。
解决:
1.
使用 VS Code 的 Live Server 插件**:
-
安装 Live Server 插件。
-
右键单击你的 HTML 文件,选择 "Open with Live Server"。
2.
(1)将动态生成的脚本转换成Blob对象。
(2)然后给这个Blob对象创建一个URL。
(3)最后将这个创建好的URL作为地址传给Worker的构造函数
// let script = 'console.log(324)'
// let workerBlob = new Blob([script], { type: "text/javascript" });
// let url = URL.createObjectURL(workerBlob);
// let worker = new Worker(url);