Node.js async.parallel,它是并行的吗?

时间:2017-09-25 03:55:27

标签: node.js asynchronous parallel-processing

我知道该节点依赖于单个事件线程。所以没有办法让它有并行线程。但async.parallel确实提供了类似并行的功能。 Stack上的另一个问题暗示async.parallel使用process.nextTick。所以基本上async.parallel只是一个并发函数而不是一个真正的并行函数?

2 个答案:

答案 0 :(得分:4)

async.parallel只是让您启动多个异步操作,然后跟踪它们何时完成。它们运行的​​真正并行完全取决于异步操作的内容。如果它们是网络操作,那么它们可能完全并行,因为涉及的CPU很少,并且处理将数据输入和输出node.js进程的CPU将在node.js单线程之外处理。

您自己处理请求结果的代码并不真正并行运行。您似乎知道,node.js是单线程的,因此您永远不会有多个自己的Javascript实际执行一次。但是,网络操作和磁盘操作等许多操作都是由单个Javascript线程之外的node.js处理的,因此可以并行完成工作。

例如,如果你将一系列同步函数传递给async.parallel,例如Javascript数学计算,那么就不会有任何并行的事情。它将运行一个完成,然后是下一个,依此类推,因为只有一个Javascript同时运行。

async.parallel将用于Promise.all()在基于承诺的设计中使用的相同情况。它可以通过多个异步操作来跟踪完成(或第一个错误)。

node.js中的同步操作的真正并行性通常通过集群(多个相同的node.js进程共享负载)或自定义子进程(启动自定义子工作程序以运行某些特定操作)完成,然后操作系统可以将多个CPU应用于不同的进程并实现一些实际的并行性。

  

我知道该节点依赖于单个事件线程。所以没有办法让它有并行线程。

在单个node.js进程中并且只运行Javascript代码(没有本机代码),这是正确的。当从本机代码中实现的Javascript调用函数时,node.js可以在内部使用线程或其他进程。

  

Stack上的另一个问题意味着async.parallel正在使用process.nextTick。

异步管理代码(异步库是这样的)通常使用process.nextTick()来强制回调始终被异步调用,以便在使用混合的同步和异步操作时产生可预测的行为。出于同样的原因,Promise规范要求.then()始终在未来的某个时间点被调用,从不同步,即使承诺立即得到解决。

  

所以基本上async.parallel只是一个并发函数而不是一个真正的并行函数?

async.parallel的目的是跟踪多个异步操作的完成情况。它本身并没有任何异步。传递给async.parallel的操作本身将是它本身的任何内容(同步或异步)。异步库不会改变它。

答案 1 :(得分:0)

我知道这并不能真正回答问题,但是我写了一篇关于使用async.parallel与worker_threads https://tech.beyondtracks.com/posts/node-worker-threads-with-async-parallel/的帖子,这可能会有所帮助,要点是您传统上可能使用async.parallel之类的

const os = require('os')
const parallelLimit = require('async/parallelLimit')

// our CPU intensive operation
function fibonacci(n) {
  if (n < 2) {
    return 1
  } else {
    return fibonacci(n - 2) + fibonacci(n - 1)
  }
}

// number of jobs to run will be the number of CPU cores
const limit = os.cpus().length

const fibonacciSize = 40

// build a set of tasks for parallelLimit to run
const tasks = Array.from({ length: limit }, (v, k) => k + 1).map((task) => {
  return cb => {
    const result = fibonacci(fibonacciSize)
    cb(null, result)
  }
})

// run tasks with parallelLimit
parallelLimit(tasks, limit, (err, results) => {
  console.log('Finished with', err, results)
})

如果要使用worker_threads在多个线程中并行运行这些任务,则可以执行以下操作:

index.js

const { Worker } = require('worker_threads')
const path = require('path')
const os = require('os')
const parallelLimit = require('async/parallelLimit')

// number of jobs to run will be the number of CPU cores
const limit = os.cpus().length

const workerScript = path.join(__dirname, './worker.js')

// build a set of tasks for parallelLimit to run
const tasks = Array.from({ length: limit }, (v, k) => k + 1).map((task) => {
  return cb => {
    const worker = new Worker(workerScript, { workerData: task })
    worker.on('message', (result) => { cb(null, result) })
    worker.on('error', (err) => { cb(err, null) })
  }
})

// run tasks with parallelLimit
parallelLimit(tasks, limit, (err, results) => {
  console.log('Finished with', err, results)
})

worker.js

const { parentPort, workerData, isMainThread } = require('worker_threads')

// our CPU intensive operation
function fibonacci(n) {
  if (n < 2) {
    return 1
  } else {
    return fibonacci(n - 2) + fibonacci(n - 1)
  }
}

const fibonacciSize = 40

if (!isMainThread) {
  const result = fibonacci(fibonacciSize)
  parentPort.postMessage(result)
}