如何在JS Web worker中循环查询worker可用性而不阻塞

时间:2016-01-04 19:33:13

标签: javascript web-worker

我对网络工作者和多线程设计都很陌生,我需要设计的是一个简单的查询任务调度程序(现在使用web worker),如:

var taskScheduler = {};
taskScheduler.pool = [];
function init(num){
    for(var i=0; i<num; i++){
        var workUnit = {};
        var worker = new Worker("worker.js");
        workUnit.busy = false;
        workUnit.worker = worker;
        taskScheduler.pool.push( workUnit );
    }
}
init(4);

下面的代码应该是循环查询workUnit availability并开始新的工作 我不太确定如何实现,我认为应该是这样的:

taskScheduler.startWork = function(task){
    for(var i=0; i<taskScheduler.pool.length; i++){
        if(!taskScheduler.pool.busy){ // fire job, make unit busy then break; }
    }
}

目前主要的挑战是:

如何在仍然能够接受新任务调用的同时继续检查工作人员的可用性(例如:如果没有可用的工作人员,它将继续询问哪个将阻止接受taskScheduler.startWork的新作业调用的能力)< / p>

4 个答案:

答案 0 :(得分:0)

这样做的一种方法是在所有工人忙碌后延迟致电startWork

taskScheduler.startWork = function(task){
    for(var i=0; i<taskScheduler.pool.length; i++){
        if(!taskScheduler.pool.busy) {
          // fire job, make unit busy then return;
        }
    }
    setTimeout(function() {
      taskScheduler.startWork(task);
    }, 500);
}

这种方式有问题

  • 工作不能保证按照要求的顺序开始
  • 有时候有自由工作者没有做任何事情
  • 管理等待工人的任务可能会很棘手(比如你需要阻止他们去找工人)

但是

  • 与队列
  • 相比更简单

更好的选择是在主线程中实现任务队列。我将此留给另一个答案: - )

答案 1 :(得分:0)

这样做的一种方法是实现任务队列。其中

  • 当有任务要完成时,它会被添加到队列
  • 尝试在2种情况下处理队列中最旧的项目

    • 将任务添加到队列
    • 当一名工人完成工作时

下面是一个简单的部分实现

taskScheduler.queue = [];
taskScheduler.addToQueue(task) {
  queue.push(task);
  taskScheduler.processQueue();
}

taskScheduler.processQueue() {
  if (!queue.length) return;
  var task = queue.shift();
  for (var i=0; i<taskScheduler.pool.length; i++) {
    if (!taskScheduler.pool.busy) {
      // fire job, make unit busy then break
    }
  }
}

function init(num) {
  ...
  worker.onmessage = function(e) {
    // Assuming a worker is finished if it passes a message back to main thread 
    workUnit.busy = false;
    processQueue();
  }
  ...
}

这样

  • 保证以添加到队列的顺序启动任务

  • 当等待任务时,工人花很少的时间不做任何事情

  • 如果需要,您可以添加逻辑来控制队列。即清除未启动的任务,重新安排订单等。

但是

  • 逻辑比其他选项稍微复杂一些。

答案 2 :(得分:0)

如果您知道每项任务将花费大约相同的时间,具体取决于您的使用情况,您可以放弃任何类型的忙碌&#34;状态,并依次向工人发射任务。每条消息都将按Javascript排队,直到每个工作程序中的堆栈都已清除

taskScheduler.nextWorkerIndex = 0;
taskScheduler.startWork = function(task){
  var worker = taskScheduler.pool[taskScheduler.nextWorkerIndex];
  taskScheduler.nextWorkerIndex = (taskScheduler.nextWorkerIndex + 1) % taskScheduler.pool.length
  // Fire job
}

这有

  • 简单逻辑

  • 如果任务花费相同的时间,那么任务可能已经发送给忙碌的工作人员,而其他人则是免费的

  • 很难管理任务队列,因为一旦发送了一条消息,直到工作人员收到该消息,它就会被Javascript代码隐藏起来。

答案 3 :(得分:0)

从一开始就检查工人的可用性是错误的概念。 Javascript是事件驱动语言。您不会轮询更改,而是使用事件监听它们。

您应该做的是以下内容:

var tasksToProcess = [/*{task: this is sent to worker, onfinish: onfinish callback}*/];
var processedTask = null;
var worker = new Worker("taskProcessor.js");
// The worker dispatches a message when task is completed
worker.addEventListener("message", function(event) {
     //Usually I give messages names to simulate Javascript event style
     if(event.data.name == "task_success") {
         if(processedTask!=null) {
             if(processedTask.onfinish instanceof Function)
                 // If onfinish throws error, it will not crash this function
                 setTimeout(processedTask.onfinish,0);
             processedTask = null;
         }
         else
             console.error("Weird, task sucessfully completed when no task was running.");
         // No matter what, try to start next task
         runNextTask();
     }
});
function runNextTask() {
    // Only run when no task is being processed
    if(processedTask==null && tasksToProcess.length>0) {
        //Shift removes and returns first value of array
        processedTask = tasksToProcess.shift();
        worker.postMessage({name:"task", task: processedTask.task});
    }
}
/// Task in format {task: task data, onfinish: finish event callback}
function addTask(task) {
    // Add task to the end
    tasksToProcess.push(task);
    runNextTask();
}

只要工作人员在完成任务后正确调用消息,这将起作用。你也应该实现Worker onerror回调,以便你可以从错误中恢复。