Javascript多线程的实现介绍

1.前言

javascript众所周知是单线程的语言,但是我们可以通过某些特殊方式来实现多线程的效果。目前我能想到的有两种方式:1.通过微任务计时器实现,2.通过whatwg定义的html5标准的worker线程来实现,下面我们来看下这两种方式的介绍吧。

2.微任务计时器版的多线程实现

多线程可以通过计时器来完成,但是计时器有缺陷,那就是执行优先级比较低,需要等待主线程执行完所有代码才会运行计时器任务,如何遇到主线程阻塞,那么计时器永远也无法执行,下面来看个例子。

  var num = 0;
    function Task() {
        this.taskId = undefined;
        this.doTask = function(callback, timeout=1000) {
            this.taskId = setInterval(callback,timeout);
            return this;
        };
        this.finishTask = function() {
            return clearInterval(this.taskId);
        }
    }
    function TaskPool(max, timeout=1000) {
        this.taskObjs = [];
        this.taskQueue = [];
        this.max = max;
        this.num = 0;
        this.callback = undefined;
        this.timeout = timeout;
        this.pushTask = function(callback, identify) {
            this.taskQueue.push({callback:callback, identify:identify});
        };
        this.run = function() {
            let that = this;
            while(this.taskQueue.length > 0) {
                if (this.num++ < max) {
                    !function doTask(){
                        //这里是异步的操作,任务队列会先弹出所有任务然后再执行任务
                        let task = that.taskQueue.pop();
                        let identify = task.identify;
                        let callback = task.callback;
                        that.taskObjs.push({identify:identify, task: new Task().doTask(callback)});
                    }();
                } else {
                    console.error('主线程无限循环,定时器微任务永远没有执行权限');
                }
            }
        };
        this.finishTask = function(identify) {
            let found = this.taskObjs.find(function(task) {
               return task.identify === identify;
            });
            if (found) {
                found.task.finishTask();
                --this.num;
            }
        }
    }
    let a = new TaskPool(2); // 任务数量超过队列上限时,主线程会无限循环而定时器则拿不到执行权
    a.pushTask(function(){
        console.log(num++, 'task1');
        if (num > 10) {
            a.finishTask('task1');
            console.log('finished task1');
        }
    }, 'task1');
    a.pushTask(function(){
        console.log(num++, 'task2');
        if (num > 20) {
            a.finishTask('task2');
            console.log('finished task2');
        }
    }, 'task2');
    a.run();

使用计时器函数是异步操作,上面的例子中,如果任务数量超过任务池的最大值,那么会陷入死循环中。

3.worker后台线程的多线程实现

worker后台线程是whatwg定义的web api,2019年以后html的标准控制权已经从W3C转到了whatwg,所以之后的html标准我们都以whatwg的为准。

let workerNums = 5;
    var data = 0;
    let finished = workerNums;
    for(let i = 0; i < workerNums; i ++) {
        let worker = new Worker('worker.js');
        worker.postMessage(data + i * 10);
        worker.onmessage = function(e) {
            data = e.data;
            if (--finished <= 0) {
                console.log('finished');
            }
        }
    }
var count = 0;
onmessage = function(e) {
    console.log('worker:' + e.data);
    var temp = e.data;
    while(count++ < 10) {
        console.log(temp++);
    }
    postMessage(temp);
    close();
}

4.两种方式的缺陷

1.定时器实现的多线程是异步的任务,执行前需要等待主线程执行完才能执行。

2.worker后台线程的多线程无法共享数据,只能将数据分片,然后再进行分片处理。

5.参考

https://html.spec.whatwg.org/multipage/workers.html#introduction-14

如无特殊说明,文章均为本站原创,转载请注明出处。如发现有什么不对的地方,希望得到您的指点。

发表评论

电子邮件地址不会被公开。 必填项已用*标注