【问题标题】:Interrupting a looping WebWorker [duplicate]中断循环的 WebWorker [重复]
【发布时间】:2016-04-26 08:06:18
【问题描述】:

我有一个专用的 Webworker,它在接收到启动信号后进入一个长循环,并且根据一些启动设置,循环将在给定的执行点“屈服”。

这是我的代码的简化版本

var mode = null;
var generator = null;

function* loop() {

    for(var i=0;i<10000;i++) {
        //Do stuff
        for(var j=0;j<10000;j++) {
            //Do stuff
            if( mode == 'inner' ){
                //Yield after each inner loop iteration
                yield 2;
            }
        }
        if( mode == 'outer' ){
            //Yield after each outer loop iteration
            yield 1;
        }
    }

    /*
    If mode is not inner or outer the function won't yield 
    and will process the whole loop in one shot
    */
    return null;

}

generator = loop();

self.onmessage = function(event) {

    var m = event.data;    
    if(m.operation == 'run') {
        mode = m.mode;
        generator.next();
    }

    if(m.operation == 'pause') {
        //Set a flag and check for it in the loop
    }
}

我想要做的是允许工作人员按需暂停,问题是在循环中工作人员不会处理消息并且不会调用 onmessage 所以我无法发送“暂停”消息设置一个标志,然后我在循环中检查该标志。

我想做的是让我的函数在每次迭代后让出,以允许工作线程处理消息队列,然后如果没有收到暂停信号,则再次恢复函数,但是,这感觉有点 hacky。

有没有办法强制 WebWorker 在不离开循环或屈服的情况下处理消息队列?或者可能是一种无需通过 onmessage() 即可设置标志的方法?

谢谢

【问题讨论】:

  • 不,你必须做其中之一 - 离开循环或让出 - 除非 JS 事件循环的核心发生了真正基本的变化。 Javascript 在一个上下文中没有多线程,因此您的循环和onmessage 不可能同时执行。如果您的平台支持yield,我建议您使用它而不是终止循环。
  • 我在想一个类似于 C# 中的 DoEvents 的函数,它可以让我让 Javascript 处理队列,这不需要多线程
  • 不,您不能从代码中调用队列。实际上,如果您考虑一下,那将是一个递归-从事件循环中调用事件循环。

标签: javascript ecmascript-6 yield web-worker


【解决方案1】:

有没有办法强制 WebWorker 在不离开循环或屈服的情况下处理消息队列?或者可能是一种无需通过 onmessage() 即可设置标志的方法?

不。 Worker 是单线程的,就像主线程一样。一次只能执行一行,幸好没有gotos。

您的解决方案也存在一个缺陷,即它不会一直使用 CPU。在loopyields 之后,它不会继续,直到你在消息事件监听器中调用.next。我建议async/await替代:

function Timeout(time) {
    return new Promise(resolve=>setTimeout(resolve, time));
}
let mode = "inner";
async function loop() {
    for(var i=0;i<10000;i++) {
        //Do stuff
        for(var j=0;j<10000;j++) {
            //Do stuff
            if( mode == 'inner' ){
                await Timeout(0);
            }
        }
        if( mode == 'outer' ){
            await Timeout(0);
        }
    }
}
// or you can start it only after you receive the right message
const workerLoop = loop();
(async () {
  await workerLoop;
  self.postMessage("done");
})();

关于暂停,你也可以使用promise:

let mode = "inner";
let pausePromise = null;
async function loop() {
    for(var i=0;i<1000;i++) {
        //Do stuff
        for(var j=0;j<1000;j++) {
            if(pausePromise) {
              console.log("Loop paused");
              await pausePromise;
              console.log("Loop resumed");
            }
        }
    }
}
let workerLoop = null;
self.onmessage = function(event) {
    var m = event.data;    
    if(m.operation == 'run') {
        mode = m.mode;
        if(!workerLoop) {
          workerLoop = loop();
        }
    }

    if(m.operation == 'pause') {
        if(workerLoop) {
          var listener = null;
          pausePromise = new Promise(resolve=>self.addEventListener("message", listener = (event)=>{
              if(event.data.operation=="run") {
                console.log("Resuming loop from promise.");
                self.removeEventListener("message", listener);
                pausePromise = null;
                resolve();
              }
          }))
        }
        else {
          console.warn("Not running!");
        }
    }
}

这有点脏,但它有效。这是它在 JS fiddle 中的工作:https://jsfiddle.net/Darker/pvy6fszL/16/

【讨论】:

    猜你喜欢
    • 2014-02-02
    • 2019-02-01
    • 2013-02-16
    • 2011-11-23
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多