Javascript 多线程编程的前世今生

2020-06-17 00:00:00 操作 线程 内存 状态 主线

为什么要多线程编程

大家看到文章的标题《Javascript 多线程编程》可能立马会产生疑问:Javascript 不是单线程的吗?Javascript IO 阻塞和其他异步的需求(例如 setTimeout, Promise, requestAnimationFrame, queueMicrotask 等)不是通过事件循环(Event Loop)来解决的吗?

没有错,Javascript 的确是单线程的,阻塞和其他异步的需求的确是通过实现循环来解决的,但是这套机制当线程需要处理大规模的计算的时候就不大适用了,试想一下一下的场景:

  1. 你需要实现对文件的加解密。
  2. 你的 VirtualDom 树有很多元素(例如上万个),你需要对这棵树进行 Diff 操作。
  3. 你需要在浏览器“挖矿”。

上面这些场景都会阻塞主线程,也就是当进行这些操作的时候,你的页面是卡住的,设置当页面卡住一段时间之后,Chrome 等浏览器或者操作系统会建议你 Kill 掉整个 Tab 或者进程。这显然不是我们想看到的事情。正因为这些场景的存在,浏览器提出了 W3C 在 2013 年提出了 Web Worker 草案,这个草案的提出就是为了解决上述这些问题。

为了让大家感受 JS 多线程能够干什么,笔者写了一个基于 Web Worker(线程)、ShareArrayBuffer(共享内存)、Atomics(锁)等 Web API 的在前端压缩和解压文件(基于 DEFLATE 算法)的 demo:

查看视频,点击 Demo 的在线地址 自己来试试吧。

Web Worker

Chrome 浏览器中每个 Tab 都是一个进程,每个进程都会有一个主线程,网页的渲染(Style, Layout, Paint, Composite)会在主线程进行操作。主线程可以发起多个 Web Worker,Web Worker 对应“线程”的概念。

每个 Web Worker 都对应一个脚本文件,主线程可以通过像以下的代码去发起多个 Web Worker,并且通过基于事件的 API 与 Web Worker 通信:

main.js

let worker = new Worker("work.js");
worker.postMessage("Hello World");
worker.onmessage = function (event) {
  console.log("Received message " + event.data);
}

相关文章