浏览器的js脚本运行一直都是单线程运行的,所以我们不需要考虑多线程同步加锁这种情况。但是当我们需要做一些比较耗时的计算时候如果还放在这个单线程里面,可以想象页面会卡主。

其实浏览器也是支持多线程运行的叫做web workers。通过web workers可以把耗时的计算放在非主线程里面。从而充分发挥电脑的性能。下图是浏览器对web worker的支持情况:

本章主要从三个方面进行研究:

1、web worker初探

2、主线程与worker线程通信

3、第三方库webworkify使用

web worker初探

创建一个webworker只需要两步:

1、一个js文件

2、new Worker

我们创建一个worker.js的文件,并计算一个求和操作,js内容如下:

let sum = 0
let num = 10000
for (let i = 0; i < num; i++) {sum = sum + i
}
console.log('worker compute sum = %d', sum)

在html中通过new Worker即可创建一个worker子线程

<script>let worker = new Worker('./worker.js')
</script>

通过上述两个步骤,即可实现一个最简单的worker子线程。并且在控制台打印出计算结果:

释放worker子线程

worker子线程会耗费大量的资源,而且不会自动的被释放,通过上述代码创建完之后,如果不需要了,我们需要手动释放。释放worker子线程有两种方法。

方法一:在html中,通过创建出来的worker对象来释放:

worker.terminate()

方法二:在worker.js中调用self.close()来释放

self.close()

主线程与worker线程通信

worker线程是一个独立的空间,与主线程之间也是完全隔离的。我们worker线程计算出来的结果想要在dom上显示必须通过主线程来操作。所以需要解决两者之间的通信问题。这里浏览器提供了onmessage和postMessage来实现两者之间的通信。

主线程通过onmessage来接收worker线程消息,通过postMessage来往worker线程发送消息。

worker线程也类似,通过onmessage来接收主线程的消息,通过postMessage来往主线程发送消息。

主线程示例:

<script>let worker = new Worker('./worker.js')worker.onmessage = (event) => {console.log(event)}worker.postMessage(`i am from main thread`)
</script>

worker线程示例:

let sum = 0
let num = 10000
for (let i = 0; i < num; i++) {sum = sum + i
}
console.log('worker compute sum = %d', sum)
self.postMessage(`worker compute sum = ${sum}`)
self.onmessage = (event) => {console.log(event)
}

运行之后,打印出来如下,我们成功的在主线程和子线程接收到了双方发过来的消息。如下图:

这种消息传递方式在正常的计算结果或者规模不大的对象传递是没有问题。但是当我们用来传递数据很大的ArrayBuffer的时候,会产生大量的内存和资源消耗。因为我们调用postMessage的时候传递的对象会重新被克隆一份出来,在onmessage函数回调的其实是另一个对象。这就是产生资源消耗变多的原因。

这里浏览器为我们提供了可转移对象来帮我们共享同一份内存。

可转移对象

可转移对象是把一个对象的所有权从主线程转移到子线程或者过程相反,这样我们就不需要来回的拷贝对象了,只是在主线程和子线程之间切换了对象的所有权。这种情况适合于某个对象每次只在一个地方使用的场景。

这种模式也是通过postMessage来传递消息,只不过需要增加第二个参数。看下面的示例:

  const arrayBuffer = new ArrayBuffer(32)worker.postMessage(arrayBuffer, [arrayBuffer])

通过上述写法,我们实现了将主线程的ArrayBuffer对象转移到了子线程。

html的示例:

<script>let worker = new Worker('./worker.js')worker.onmessage = (event) => {console.log(event)}const arrayBuffer = new ArrayBuffer(32)worker.postMessage(arrayBuffer, [arrayBuffer])console.log(arrayBuffer)
</script>

worker.js的示例:

let sum = 0
let num = 10000
for (let i = 0; i < num; i++) {sum = sum + i
}
console.log('worker compute sum = %d', sum)
self.postMessage(`worker compute sum = ${sum}`)
self.onmessage = (event) => {console.log(event)
}
const arrayBuffer = new ArrayBuffer(64)
self.postMessage(arrayBuffer, [arrayBuffer])
console.log(arrayBuffer)

看下我们的输出:

我们输出的arrayBuffer都是一个空对象,因为里面的内容被转移了。

webworkify

webworkify是一个npm包,通过这个包我们可以在worker.js中使用require()来引用其他module的函数。我们不可能通过一个worker.js就把业务都能搞定,而且现在前端都是模块化的开发,所以webworkify的作用还是很大的。

github地址:

https://github.com/browserify/webworkify

安装webworkify

npm install webworkify

我们对前面的累计求和进行一下改造,新建一个add.js的文件并写一个add的函数:

class AddTest {add(a, b) {return a + b}
}
module.exports = AddTest

然后我们在worker.js中通过require引入这个对象,并把求和计算换成add函数。worker.js的写法如下,其他都没变,增加了module.exports和修改了求和计算。

var AddTest = require('./add');module.exports = function (self) {let addTest = new AddTest()let sum = 0let num = 10000for (let i = 0; i < num; i++) {sum = addTest.add(sum, i)}console.log('worker compute sum = %d', sum)self.postMessage(`worker compute sum = ${sum}`)self.onmessage = (event) => {console.log(event)}const arrayBuffer = new ArrayBuffer(64)self.postMessage(arrayBuffer, [arrayBuffer])console.log(arrayBuffer)
}

在写一个main.js来创建子线程,在这里我们引入了webworkify来替代原先默认的Worker。

var work = require('webworkify');
var worker = work(require('./worker.js'));
worker.addEventListener('message', (event) => {console.log(event)
})
const arrayBuffer = new ArrayBuffer(32)
worker.postMessage(arrayBuffer, [arrayBuffer])
console.log(arrayBuffer)

最后通过browserify来打包,Browserify 可以让你使用类似于 node 的 require() 的方式来组织浏览器端的 Javascript 代码,通过预编译让前端 Javascript 可以直接使用 Node NPM 安装的一些库。

这里我们使用命令:

browserify main.js > bundle.js

最后在html中引入bundle.js文件即可。

<body><script src="bundle.js"></script>
</body>

最后的运行结果和前面的是一样的:

但是我们通过webworkify来组织子线程的代码可以在子线程中进行模块化编程,提高代码的可读性。

写在最后:

可以关注本人公众号:

迷途小书童爱读书

或者扫描如下二维码:

回复webworkers

即可获得

1、原生webworker示例

2、基于webworkify的示例

web多线程之webworkers相关推荐

  1. linux 线程pthread_detach,linux线程之pthread_join和pthread_detach

    在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在 被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反 ...

  2. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  3. Asp.Net Core 轻松学-多线程之Task快速上手

    Asp.Net Core 轻松学-多线程之Task快速上手 原文:Asp.Net Core 轻松学-多线程之Task快速上手 前言     Task是从 .NET Framework 4 开始引入的一 ...

  4. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  5. Java多线程之Callable、Future和FutureTask

    Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...

  6. Java多线程之Synchronized和Lock的区别

    Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...

  7. Java多线程之CAS缺点

    Java多线程之CAS缺点 目录: 循环时间开销很大 只能保证一个共享变量的原子操作 引来ABA问题及解决方案(重点) 1. 循环时间开销很大 通过看源码,我们发现有个do while,如果CAS失败 ...

  8. Java多线程之CAS深入解析

    Java多线程之CAS深入解析 目录: CAS是什么 CAS底层原理Unsafe深入解析 CAS缺点 引子:蚂蚁花呗一面:讲一讲AtomicInteger,为什么要用CAS而不是synchronize ...

  9. Java多线程之volatile详解

    Java多线程之volatile详解 目录: 什么是volatile? JMM内存模型之可见性 volatile三大特性之一:保证可见性 volatile三大特性之二:不保证原子性 volatile三 ...

最新文章

  1. 数据存储之属性列表Plist
  2. iOS -OC调用js页面
  3. 系统接口502异常_基于SpringBoot2.0的后台权限管理系统
  4. android常见面试问题
  5. Opencv——霍夫变换以及遇到的一些问题
  6. java biginteger使用_在Java中使用BigInteger值
  7. 专技继续教育-使用tampermonkey刷课时(可自动跳转下一节)
  8. Python微信公众号后台开发教程001
  9. mysql复制表结构(包括索引)
  10. python 随机产生一个整数 并猜测该数字小游戏
  11. android 动画遮盖,android view用动画实现遮罩效果
  12. 三冲IPO,独角兽唱吧能成为“K歌第一股”吗?
  13. JAVA有percentile函数吗_计算DAX中的PERCENTILE
  14. Vue3必会技巧-自定义Hooks
  15. codecombat 代码攻略
  16. 1.删除链表的奇数节点 2.删除链表的偶数节点
  17. [4G5G基础学习]:流程 - 4G LTE PLMN选择、扫频、小区搜索、系统消息读取、小区选择过程
  18. android百度地图行政区填充颜色
  19. Python KMeans聚类分析
  20. EBS 分配指定快码维护权限

热门文章

  1. POJ-1163(DP,Water)
  2. Node.js 提升运行效率
  3. 石家庄计算机学院生活费每月多少,石家庄高校每月生活费约1000元
  4. BP神经网络参数设置及实例
  5. .Net操作Excel后彻底释放资源
  6. Ajax最全Resful中@PathVariable占位使用前台发请求服务器接收方法流程及参数传递方
  7. 如何从基础到深入了解java构造器
  8. 输出nc数据_NetCDF(NC)数据的使用、转换和分析
  9. python socket 域名_python用socket发送http请求
  10. winfrom datagridview中DataGridViewTextBoxColumn的联动处理