node多线程实现,node js 多线程
异步计算怎么做?下面这篇文章介绍了利用浏览器和Node.js的多线程能力进行异步计算的方法,希望对你有所帮助!
node.js速度课程简介:进入学习
都说Node.js可以实现高性能服务器,那么什么是高性能呢?
所有的软件代码最终都要通过CPU来运行。CPU能否高效使用是区分性能高低的标志,也就是说不能闲置。[推荐研究:《nodejs 教程》]
什么时候会闲置?
当程序在做网络和磁盘IO的时候,CPU是空闲的,也就是空转。多核CPU可以同时运行多个程序。如果只使用一个内核,则其他内核处于空闲状态。因此,为了实现高性能,我们必须解决这两个问题。
操作系统提供了线程的抽象,不同代码对应的执行分支都可以同时运行在不同的CPU上,这是一种利用众多核心CPU性能的方式。
但是如果有些线程在进行IO,也就是等待读写完成被阻塞,这是一种相对低效的方式,所以操作系统实现了DMA的机制,也就是设备控制器,由硬件负责从设备到内存的传输,完成后告诉CPU。这样,当一些线程处于IO时,可以挂起线程,然后在收到DMA传输数据完成的通知后继续运行。
多线程,DMA,这是操作系统提供的一种解决方案,它利用众核CPU来解决CPU拥塞等IO问题。
而且各种编程语言都封装了这个机制,Node.js也是,Node.js之所以性能高,是因为异步IO的设计。
Node.js的异步IO是在libuv中实现的,基于操作系统提供的异步系统调用,一般是硬件层面的异步,比如DMA来传输数据。但是有些同步的系统调用被libuv封装后会变成异步的。这是因为libuv中有一个线程池来执行这些任务,并使同步API异步。这个线程池的大小可以通过UV_THREADPOOL_SIZE的环境变量来设置,默认值是4。
我们在代码中调用的许多异步API都是通过线程实现的。
例如:
const fsPromises=require(fs )。承诺;
const data=await fs promises . readfile(。/filename’);但是这个异步API只是解决了IO的问题,那么如何利用多核CPU的优势做计算呢?
Node.js在10.5中实验性的引入了worker_thread模块(12年正式引入),可以创建线程,最后用多个CPU运行。这是使用多核CPU进行计算的方式。
异步API可以使用多线程做IO,而worker_thread可以创建线程做不同用途的计算。
要说清楚worker_thread,我们得从浏览器的web worker说起。
浏览器的 web worker
浏览器也面临着无法使用多核CPU做计算的问题,所以html5引入了web worker,可以通过另一个线程做计算。
!声明文档类型
超文本标记语言
头/头
身体
脚本
(异步函数(){
const res=await runCalcWorker(2,3,3,3);
console . log(RES);
})();
函数runCalcWorker(.nums) {
返回新承诺((解决,拒绝)={
const calcWorker=新工人(。/web worker . js’);
calcWorker.postMessage(nums)
calc worker . on message=function(msg){
resolve(msg . data);
};
calc worker . on error=reject;
});
}
/脚本
/body
/html我们创建一个Worker对象,指定在另一个线程中运行的js代码,然后通过postMessage向其传递消息,通过onMessage接收消息。这个过程也是异步的,我们进一步把它封装成promise。
然后接收webWorker.js中的数据,进行计算,然后通过postMessage返回结果。
//webWorker.js
onmessage=function(msg) {
if (Array.isArray(msg.data)) {
const RES=msg . data . reduce((total,cur)={
return total=cur
}, 0);
后期消息(RES);
}
}就这样,我们用了另一个CPU核来运行这个计算,和普通异步代码写代码没什么区别。但这个异步其实不是IO的异步,而是计算的异步。
Node.js的Worker thread类似于web worker,我甚至怀疑worker thread的名字是受了web worker的影响。
Node.js 的 worker thread
如果在Node.js中实现上述异步计算逻辑,它是这样的:
const runCalcWorker=require(。/runCalcWorker’);
(异步函数(){
const res=await runCalcWorker(2,3,3,3);
console . log(RES);
})();异步调用,因为异步计算和异步IO在用法上没有区别。
//runCalcWorker.js
const { Worker }=require( Worker _ threads );
module.exports=函数(.nums) {
返回新承诺(功能(解决,拒绝){
const calcWorker=新工人(。/nodeworker . js’);
calc worker . postmessage(nums);
calcWorker.on(message ,resolve);
calcWorker.on(错误,拒绝);
});
}然后异步计算是通过创建一个Worker对象,指定在另一个线程中运行的JS,然后通过postmessage传递消息,通过message接收消息来实现的。这与web worker非常相似。
//nodework . js
常数{
父端口
}=require( worker _ threads );
parentPort.on(message ,(data)={
const res=data.reduce((total,cur)={
return total=cur
}, 0);
parent port . postmessage(RES);
});在执行计算的nodeWorker.js中,监听消息message,然后计算并通过parentPost.postMessage返回数据
与网络工作者相比,你会发现一些特别的东西。所以我认为Node.js的worker thread的api是参考web worker设计的。
然而,实际上worker thread在创建时也支持通过wokerData进行数据传输:
const { Worker }=require( Worker _ threads );
module.exports=函数(.nums) {
返回新承诺(功能(解决,拒绝){
const calcWorker=新工人(。/nodework . js ,{
workerData: nums
});
calcWorker.on(message ,resolve);
calcWorker.on(错误,拒绝);
});
}然后工作线程通过workerData处理它:
常数{
parentPort,
工人数据
}=require( worker _ threads );
const data=workerData
const res=data.reduce((total,cur)={
return total=cur
}, 0);
parent port . postmessage(RES);因为有传递消息的机制,所以不能序列化的数据(如函数)不能被传输进行序列化和反序列化。这也是worker thread的特点。
Node.js 的 worker thread 和 浏览器 web woker 的对比
从使用的角度来说,可以封装成普通的异步调用,和其他异步API没什么区别。
都需要序列化和反序列化,都支持postMessage和onMessage发送和接收消息。
除了message,Node.js的worker线程还支持更多传输数据的方式,比如workerData。
但本质上两者都是为了实现异步计算,充分利用多核CPU的性能。没有区别。
总结
高性能程序要充分利用CPU资源,不要让它闲置,也就是不要让CPU等待IO,多核CPU要能同时利用它做计算。操作系统提供了线程和DMA机制来解决这个问题。Node.js也做了相应的封装,也就是libuv实现的异步IO的api。然而,计算的异步是由节点12,即工作线程正式引入的。api设计指的是浏览器的web worker。通过postMessage和onMessage传递消息时需要对数据进行序列化,所以不能传递函数。
从使用的角度来看,异步计算和异步IO的使用方式是一样的,但异步IO只是让cpu在不同的块中等待IO完成。异步计算利用多核CPU同时进行并行计算,计算性能提升数倍。
有关编程的更多信息,请访问:编程视频!以上是讲如何利用Node.js的多线程能力做异步计算的细节,更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。