客户端JavaScript线程池设计的示例分析(javascript,开发技术)

时间:2024-05-10 05:30:23 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

1.介绍:

本打算在客户端JavaScript进行机器学习算法计算时应用线程池来优化,就像()演示的神经网络。但是由于各种原因不了了之了。本次遇到了一个新的问题,客户端的MD5运算也是耗时操作,如果同时对多个字符串或文件进行MD5加密就可以使用线程池来优化。

2.准备工作:

到npm官网搜索spark-md5,到其github仓库下载spark-md5.js。该js文件支持AMD,CommonJS和web工作线程的模块系统,我们在实现线程池时,线程工作代码交给web工作线程处理。

客户端JavaScript线程池设计的示例分析

客户端JavaScript线程池设计的示例分析

客户端JavaScript线程池设计的示例分析

3.测试spark-md5是否正常工作:

创建一个网页,再创建一个worker.js用于保存工作线程的代码。以下述代码测试,如果成功输出MD5编码,那么准备工作完成。

客户端网页代码

<script>letworker=newWorker("worker.js")worker.postMessage("Danny")worker.onmessage=function({data}){console.log(data)worker.terminate()}</script>

工作线程代码

self.importScripts("spark-md5.js")self.onmessage=function({data}){self.postMessage(self.SparkMD5.hash(data))}

4.线程池设计

1. 目标:本次线程池设计的目标是初始创建n个初始线程,能够满足任意个线程请求,超出n的请求并不丢弃,而是等待到出现空闲线程后再分配之。

2. 基本设计思路:为了基本满足上述目标,至少要有一个线程分配功能,一个线程回收功能。

3. 线程分配功能设计:

  • 线程池满指的是线程池已经没有可用空闲线程

  • 通知对象是一个不可逆状态机,可以用Promise对象来实现

  • 阻塞请求队列存储Promise对象的resolve方法即可

  • 存储线程池中的线程使用数组即可,数组每个元素是一个对象,包括线程和线程状态

  • 返回给用户的可用线程还需要有线程在数组中的下标,在线程释放中会用到

客户端JavaScript线程池设计的示例分析

4. 线程释放功能设计:

  • 线程释放功能需要接收一个参数,为线程的标识,3中设计该标识为数组下标

  • 当线程释放后,查看阻塞请求队列是否为空,如果不为空,说明有被阻塞的线程请求,此时令队首元素出队即可,执行resolve()通知对象的状态变更为Fulfilled

客户端JavaScript线程池设计的示例分析

5. 实现线程池:

classMD5Pool{//worker用于存储线程worker=[]//status是线程池状态status="Idle"//阻塞请求队列blockRequestQueue=[]//size为用户希望的线程池的容量constructor(size){for(leti=0;i<size;i++)this.worker.push({worker:newWorker("worker.js"),status:"Idle"})}//线程池状态更新函数statusUpdate(){letsum=0this.worker.forEach(({status})=>{if(status==="Busy")sum++})if(sum===this.worker.length)this.status="Busy"elsethis.status="Idle"}//线程请求方法assign(){if(this.status!=="Busy"){//此时线程池不满,遍历线程,寻找一个空闲线程for(leti=0;i<this.worker.length;i++)if(this.worker[i].status==="Idle"){//该线程空闲,更新状态为忙碌this.worker[i].status="Busy"//更新线程池状态,如果这是最后一个空闲线程,那么线程池状态变为满this.statusUpdate()//返回给用户该线程,和该线程的标识,标识用数组下标表示return{worker:this.worker[i].worker,index:i}}}else{//此时线程池满letresolve=null//创建一个通知对象letpromise=newPromise(res=>{//取得通知对象的状态改变方法resolve=res})//通知对象的状态改变方法加入阻塞请求队列this.blockRequestQueue.push(resolve)//返回给请求者线程池已满信息和通知对象return{info:"full",wait:promise}}}//线程释放方法,接收一个参数为线程标识release(index){this.worker[index].status="Idle"//阻塞请求队列中的第一个请求出队,队列中存储的是promise的resolve方法,此时执行,通知请求者已经有可用的线程了if(this.blockRequestQueue.length)//阻塞请求队列队首出列,并执行通知对象的状态改变方法this.blockRequestQueue.shift()()//更新线程池状态,此时一定空闲this.status="Idle"}}

5.spark-md5对文件进行md5编码

说明:

在3的测试中spark-md5只是对简单字符串进行MD5编码,并非需要大量运算的耗时操作。spark-md5可以对文件进行MD5编码,耗时较多,实现如下。

注意:

spark-md5对文件编码时必须要对文件进行切片后再加密整合,否则不同文件可能会有相同编码。详情见github或npm。

//在工作线程中引入spark-md5self.importScripts("spark-md5.js")letfd=newFileReader()letspark=newself.SparkMD5.ArrayBuffer()//接收主线程发来的消息,是一个文件self.onmessage=function(event){//获取文件letchunk=event.data//spark-md5要求计算文件的MD5必须切片计算letchunks=fileSlice(chunk)//计算MD5编码load(chunks)}//切片函数functionfileSlice(file){letpos=0letchunks=[]//将文件平均切成10分计算MD5constSLICE_SIZE=Math.ceil(file.size/10)while(pos<file.size){//slice可以自动处理第二个参数越界chunks.push(file.slice(pos,pos+SLICE_SIZE))pos+=SLICE_SIZE}returnchunks}//MD5计算函数asyncfunctionload(chunks){for(leti=0;i<chunks.length;i++){fd.readAsArrayBuffer(chunks[i])//在这里希望节约空间,因此复用了FileReader,而不是每次循环新创建一个FileReader。需要等到FileReader完成read后才可以进行下一轮复用,因此用await阻塞。awaitnewPromise(res=>{fd.onload=function(event){spark.append(event.target.result)if(i===chunks.length-1){self.postMessage(spark.end())}res()}})}}

6.大量文件进行MD5加密并使用线程池优化

下面的测试代码就是对上文所述的拼接

网页代码

<inputid="input"type="file"multipleonchange="handleChanged()"/><body><script>classMD5Pool{worker=[]status="Idle"blockRequestQueue=[]constructor(size){for(leti=0;i<size;i++)this.worker.push({worker:newWorker("worker.js"),status:"Idle"})}statusUpdate(){letsum=0this.worker.forEach(({status})=>{if(status==="Busy")sum++})if(sum===this.worker.length)this.status="Busy"elsethis.status="Idle"}assign(){if(this.status!=="Busy"){for(leti=0;i<this.worker.length;i++)if(this.worker[i].status==="Idle"){this.worker[i].status="Busy"this.statusUpdate()return{worker:this.worker[i].worker,index:i}}}else{letresolve=nullletpromise=newPromise(res=>{resolve=res})this.blockRequestQueue.push(resolve)return{info:"full",wait:promise}}}release(index){this.worker[index].status="Idle"//阻塞请求队列中的第一个请求出队,队列中存储的是promise的resolve方法,此时执行,通知请求者已经有可用的线程了if(this.blockRequestQueue.length)this.blockRequestQueue.shift()()this.status="Idle"}}//input点击事件处理函数functionhandleChanged(){letfiles=event.target.files//创建一个大小为2的MD5计算线程池letpool=newMD5Pool(2)//计算切片文件的MD5编码Array.prototype.forEach.call(files,file=>{getMD5(file,pool)})}//获取文件的MD5编码的函数,第一个参数是文件,第二个参数是MD5线程池asyncfunctiongetMD5(chunk,pool){letthread=pool.assign()//如果info为full,那么说明线程池线程已被全部占用,需要等待if(thread.info==="full"){//获取线程通知对象letwait=thread.wait//等到wait兑现时说明已经有可用的线程了awaitwaitthread=pool.assign()let{worker,index}=threadworker.postMessage(chunk)worker.onmessage=function(event){console.log(event.data)pool.release(index)}}else{let{worker,index}=threadworker.postMessage(chunk)worker.onmessage=function(event){console.log(event.data)pool.release(index)}}}</script></body>

工作线程代码

self.importScripts("spark-md5.js")letfd=newFileReader()letspark=newself.SparkMD5.ArrayBuffer()self.onmessage=function(event){//获取文件letchunk=event.data//spark-md5要求计算文件的MD5必须切片计算letchunks=fileSlice(chunk)//计算MD5编码load(chunks)}//切片函数functionfileSlice(file){letpos=0letchunks=[]//将文件平均切成10分计算MD5constSLICE_SIZE=Math.ceil(file.size/10)while(pos<file.size){//slice可以自动处理第二个参数越界chunks.push(file.slice(pos,pos+SLICE_SIZE))pos+=SLICE_SIZE}returnchunks}//MD5计算函数asyncfunctionload(chunks){for(leti=0;i<chunks.length;i++){fd.readAsArrayBuffer(chunks[i])//在这里希望节约空间,因此复用了FileReader,而不是每次循环新创建一个FileReader。需要等到FileReader完成read后才可以进行下一轮复用,因此用await阻塞。awaitnewPromise(res=>{fd.onload=function(event){spark.append(event.target.result)if(i===chunks.length-1){self.postMessage(spark.end())}res()}})}}

随机选取18个文件进行MD5编码,结果如下

客户端JavaScript线程池设计的示例分析

JavaScript的作用是什么

1、能够嵌入动态文本于HTML页面。2、对浏览器事件做出响应。3、读写HTML元素。4、在数据被提交到服务器之前验证数据。5、检测访客的浏览器信息。6、控制cookies,包括创建和修改等。7、基于Node.js技术进行服务器端编程。

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:客户端JavaScript线程池设计的示例分析的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:vue怎么解决数据加载时插值表达式闪烁的问题下一篇:

4 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18