文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-06-28 23:29

关注

这篇“客户端JavaScript线程池设计的示例分析”除了程序员外大部分人都不太理解,今天小编为了让大家更加理解“客户端JavaScript线程池设计的示例分析”,给大家总结了以下内容,具有一定借鉴价值,内容详细步骤清晰,细节处理妥当,希望大家通过这篇文章有所收获,下面让我们一起来看看具体内容吧。

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>    let worker = new Worker("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. 线程分配功能设计:

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

4. 线程释放功能设计:

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

5. 实现线程池:

class MD5Pool {    // worker用于存储线程    worker = []    // status是线程池状态    status = "Idle"    // 阻塞请求队列    blockRequestQueue = []    // size为用户希望的线程池的容量    constructor(size) {        for(let i = 0; i < size; i ++)            this.worker.push({                worker: new Worker("worker.js"),                status: "Idle"            })    }        // 线程池状态更新函数    statusUpdate() {        let sum = 0        this.worker.forEach(({ status }) => {            if(status === "Busy")                sum ++        })        if(sum === this.worker.length)            this.status = "Busy"         else            this.status = "Idle"    }        // 线程请求方法    assign() {        if(this.status !== "Busy") {            // 此时线程池不满,遍历线程,寻找一个空闲线程            for (let i = 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 {            // 此时线程池满            let resolve = null            // 创建一个通知对象            let promise = new Promise(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")let fd = new FileReader()let spark = new self.SparkMD5.ArrayBuffer()// 接收主线程发来的消息,是一个文件self.onmessage = function(event) {    // 获取文件    let chunk = event.data    // spark-md5要求计算文件的MD5必须切片计算    let chunks = fileSlice(chunk)    // 计算MD5编码    load(chunks)}// 切片函数function fileSlice(file) {    let pos = 0    let chunks = []    // 将文件平均切成10分计算MD5    const SLICE_SIZE = Math.ceil(file.size / 10)    while(pos < file.size) {        // slice可以自动处理第二个参数越界        chunks.push(file.slice(pos, pos + SLICE_SIZE))        pos += SLICE_SIZE    }    return chunks}// MD5计算函数async function load(chunks) {    for(let i = 0; i < chunks.length; i ++) {        fd.readAsArrayBuffer(chunks[i])        // 在这里希望节约空间,因此复用了FileReader,而不是每次循环新创建一个FileReader。需要等到FileReader完成read后才可以进行下一轮复用,因此用await阻塞。        await new Promise(res => {            fd.onload = function(event) {                spark.append(event.target.result)                if(i === chunks.length - 1) {                    self.postMessage(spark.end())                }                res()            }        })    }}

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

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

网页代码

<input id="input" type="file" multiple onchange="handleChanged()"/><body>    <script>        class MD5Pool {            worker = []            status = "Idle"            blockRequestQueue = []            constructor(size) {                for(let i = 0; i < size; i ++)                    this.worker.push({                        worker: new Worker("worker.js"),                        status: "Idle"                    })            }            statusUpdate() {                let sum = 0                this.worker.forEach(({ status }) => {                    if(status === "Busy")                        sum ++                })                if(sum === this.worker.length)                    this.status = "Busy"                 else                    this.status = "Idle"            }            assign() {                if(this.status !== "Busy") {                    for (let i = 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 {                    let resolve = null                    let promise = new Promise(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点击事件处理函数        function handleChanged() {            let files = event.target.files            // 创建一个大小为2的MD5计算线程池            let pool = new MD5Pool(2)            // 计算切片文件的MD5编码            Array.prototype.forEach.call(files, file => {                getMD5(file, pool)            })        }        // 获取文件的MD5编码的函数,第一个参数是文件,第二个参数是MD5线程池        async function getMD5(chunk, pool) {            let thread = pool.assign()            // 如果info为full,那么说明线程池线程已被全部占用,需要等待            if(thread.info === "full") {                // 获取线程通知对象                let wait = thread.wait                // 等到wait兑现时说明已经有可用的线程了                await wait                thread = pool.assign()                let { worker, index } = thread                worker.postMessage(chunk)                worker.onmessage = function (event) {                    console.log(event.data)                    pool.release(index)                }            } else {                let { worker, index } = thread                worker.postMessage(chunk)                worker.onmessage = function (event) {                    console.log(event.data)                    pool.release(index)                }            }        }    </script></body>

工作线程代码

self.importScripts("spark-md5.js")let fd = new FileReader()let spark = new self.SparkMD5.ArrayBuffer()self.onmessage = function(event) {    // 获取文件    let chunk = event.data    // spark-md5要求计算文件的MD5必须切片计算    let chunks = fileSlice(chunk)    // 计算MD5编码    load(chunks)}// 切片函数function fileSlice(file) {    let pos = 0    let chunks = []    // 将文件平均切成10分计算MD5    const SLICE_SIZE = Math.ceil(file.size / 10)    while(pos < file.size) {        // slice可以自动处理第二个参数越界        chunks.push(file.slice(pos, pos + SLICE_SIZE))        pos += SLICE_SIZE    }    return chunks}// MD5计算函数async function load(chunks) {    for(let i = 0; i < chunks.length; i ++) {        fd.readAsArrayBuffer(chunks[i])        // 在这里希望节约空间,因此复用了FileReader,而不是每次循环新创建一个FileReader。需要等到FileReader完成read后才可以进行下一轮复用,因此用await阻塞。        await new Promise(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技术进行服务器端编程。

感谢您的阅读,希望您对“客户端JavaScript线程池设计的示例分析”这一关键问题有了一定的理解,具体使用情况还需要大家自己动手实验使用过才能领会,快去试试吧,如果想阅读更多相关知识点的文章,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯