线程 vs 过程、并行 vs 并发
在一个程序当中,能够有多个过程。在一个过程里,也能够有多个线程。内存在过程间是不共享的,而在线程里,是能够共享的。通过下图,你能够有更直观的了解。
并发是同一时间段进行的工作,而并行是两个在同一时刻进行的工作。除非是多核 CPU,否则如果只有一个 CPU 的话,多线程自身并不反对并行,而只能通过线程间的切换执行来做到并发。从下图中,你能够对并发和并行有更直观的意识。
前后端语言如何反对线程并行开发
如果单从语言的层面来看,JavaScript 自身没有线程的标准,也没有内置的对外裸露给开发者用来创立线程的接口。所以 JavaScript 自身的执行是单线程的。
iOS、安卓和 unity 的开发中,开发者别离有 GradeCentralDispatch、WorkManager 和 Job System 这些反对并行开发的弱小工具包。之所以这些语言不仅反对多线程,还助力并行的开发。是因为这些语言所编写的程序都和前端用户有深度的交互。
和前端不同,在很多的后端高级语言,如 Ruby 或 Python 中,无论 CPU 是单核还是多核,线程都是被全局解释器锁(GIL,global interpreter lock)限度并行的,而只反对并发。
JavaScript 中的多线程并行
开发从下面的例子中,咱们能够看到,前端语言对线程和并行的反对,总体要优于后端。
JavaScript 尽管自带函数式编程的反对,而且在这几年增强了响应式编程的设计思维,然而在多线程和并行开发方面的能力还是有缺失的,然而这并不代表多线程在前端齐全不能实现。
JavaScript 中的异步
因为近些年中,JavaScript 中大多数的所谓“多任务处理”,都是通过将工作拆分成工作单元后异步执行的。这些不同的工作能够被看做是某种“并发”。
这里的工作拆分,就是通过咱们上节讲过的事件 callback 回调和 promise,将工作分成异步的工作单元。在异步的场景中,同一时间只有一个调用栈。
事件 callback 回调、promise、await、用户事件和 timeout 都是采纳工作拆分的原理。
也就是说只有一个 JavaScript 指令完结后,下一个指令能力执行,所以异步也是有老本的,说响应和渐进式依然是在串行的根底上操作,而不是在并行的根底上实现的。
JavaScript 中用 Web Worker 反对并行
WebWorker。它能够突破帧同步,容许咱们在和主线程并行的工作线程上执行其它的指令。
*咱们晓得的 Chrome、Safari 和 FireFox 等浏览器都有本人的虚机来实现 JavaScript。这和咱们在前端用到的文件系统、网络、setTimeout、设施等性能一样,都是由嵌入环境的 Node 或浏览器虚机提供,而不是语言自身提供的。所以多线程的接口也是一样,也是由浏览器提供的。而浏览器或虚机提供的反对多线程的 API 就是 Web Worker。
要创立一个 Web Worker 非常简单,咱们只须要相似上面的一个 new 语句。之后,如果能够通过 postMessage 在 main.js 和 worker.js 之间传递信息,单方也能够通过 onMessage 来接管来自对方的音讯。*
// main.jsvar worker = new Worker('worker.js');worker.postMessage('Hello world');worker.onmessage = (msg) => { console.log('message from worker', msg.data);}// worker.jsself.onmessage = (msg) => { postMessage('message sent from worker'); console.log('message from main', msg.data);}
在 JavaScript 中,有几种不同的工作线程,别离为 dedicated worker、shared worker 和 service worker。咱们能够别离来看看它们的作用。这里咱们先来看看 dedicated worker。
dedicated worker 只在一个 realm 中能够应用。它也能够有很多的层级,然而如果层级过多,可能会引起逻辑的凌乱,所以在应用上还是要留神。
和 dedicated worker 绝对的是 shared worker,顾名思义,如果说 dedicated 是专属的,那么 shared 则是可共享的,所以 shared worker 能够被不同的标签页、iframe 和 worker 拜访。但 shared worker 也不是没有限度,它的限度是只能被在同源上跑的 JavaScript 拜访。
service worker,它既然叫做服务,那就和后端无关。它的特点就是在前端的页面都敞开的状况下,也能够运行。
信息传递模式
结构化拷贝算法
通过先拷贝再传递的形式。这里应用到的一个拷贝算法就是相似咱们之前说到的深拷贝,也叫做结构化拷贝(structured clone)。
申请和反馈模式
当咱们要传递更简单的数据结构时,比方如果咱们须要传递的是上面这样一个带有参数的函数调用,在这个时候,咱们须要先将函数调用转化为一个序列,也就是一个对应的是咱们的本地调用近程过程调用,叫做 PRC(Remote Procedure Call)。
基于 postMessage 异步的个性,它返回的不是一个后果,而是一个 await 的 promise。
isOdd(3);is_odd|num:3worker.postMessage('is_odd|num:3');worker.postMessage('is_odd|num:5');worker.onmessage = (result) => { // 'true' // 'false'};
结构化拷贝算法反对除了 Symbol 以外的其它类型的原始数据,这里蕴含了布尔、null、undefined、数字、BigInt 和咱们用到的字符串。结构化拷贝算法也能够反对多种的数据结构,包含数组、字典和汇合等。然而函数(function)和(class)类是不能通过 postMessage 来传递的。
命令和派发模式
通过开发者本人实现的一个来保障指令派发的逻辑就叫做 command dispatcher 模式。
var commands = { isOdd(num) { /*...*/ }, isEven(num) { /*...*/ } }; function dispatch(method, args) { if (commands.hasOwnProperty(method)) { return commands[method](...args); } //...}
此文章为2月Day7学习笔记,内容来源于极客工夫《Jvascript进阶实战课》,大家共同进步