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