首页 > 解决方案 > 这是处理节点中长时间运行的同步/阻塞任务的更好方法

问题描述

所以假设我们想在一个非常长的数组中找到所有数字的总和。有两种方法可以在不阻塞事件循环的情况下做到这一点。

  1. 使用子进程使用节点

  2. 创建一个异步函数来添加并使用它来查找所有数字的总和。

    import awaitEach from 'await-each';
    
    
    function makeItAsync(myFunc){
     return function(...args){
    
       return new Promise(function(resolve,reject){
         setImmediate(function(){
           var result = myFunc(...args);
           resolve(result);
         });
      });
    
     }
    }
    
    function add(a,b){ return a + b}
    
    var asyncAdd = makeItAsync(add);
    
    var list = [1,2,3,4,5,6,7,8,9.........];
    var sum = 0;
    
    await awaitEach(list, async function(n){
        sum = await asyncAdd(n,sum);
    })
    
    console.log(sum);
    

在数组中使用 asyncAdd 和异步循环可能是在不阻塞的情况下执行长时间运行任务的解决方案之一。

想知道哪个在性能(cpu,内存)方面更好。

标签: node.jschild-processevent-loop

解决方案


setImmediate()并且setTimeout()可能看起来使某些事情异步,因为它们不会立即阻塞,但它们并没有真正解决长时间运行的阻塞任务的问题。他们安排任务稍后运行,但是当它运行时,它仍然是一个长时间运行的任务,会阻塞事件循环直到它完成。因此,您的makeItAsync()函数实际上并不比仅使用setTimeout().

如果您有一个长时间运行的任务并且您不想长时间阻塞事件循环,那么这些是您的主要选择:

  1. 将长时间运行的任务分解成非常小的工作,每个工作都可以在几毫秒内完成。在你完成一项工作后,用一个简短的时间安排下一项工作,setTimeout()这将允许事件循环处理你的工作之间的其他事件。这通常需要对长时间运行的任务(几乎进入状态机)进行重大重写,并且有时对于某些类型的处理可能很难架构和维护。

  2. 启动一个子进程并将长时间运行的任务外包给子进程。然后,您的主事件循环完全没有长时间运行的任务。子进程可以在完成后返回结果。如果您必须经常运行此任务,您可以创建一个工作队列和一组子进程,这些进程在队列中的任务上进行咀嚼,并在完成时向父进程发送一条消息。这通常很容易做到。曾经的缺点是子进程无法直接访问主进程中的数据,因此如果长时间运行的任务需要访问主进程中的大量数据,那么您必须设计一种共享数据的方法(也许将数据放入另一个进程(例如 redis)中,多个进程可以访问它)。


推荐阅读