当前位置: 首页 > 科技观察

Node.js知识——如何实现线程休眠?

时间:2023-03-15 22:36:15 科技观察

本文转载自微信公众号《Nodejs技术栈》,作者吴跃军。转载本文请联系Nodejs技术栈公众号。Node.js小知识记录一些工作中遇到的问题或者在“Nodejs技术栈”交流群。有时候一个小问题可以扩展出很多新的知识点。解决问题和总结的过程本身也是一个成长的过程,在这里和大家分享一下成长。使用JavaScript/Node.js的开发者如果遇到需要延迟的任务,可能会有疑问🤔?为什么Java中没有像Thread.sleep()那样实现线程休眠的方法?在Node.js中实现一个sleep()函数。一:坏的“循环空转”下面的一段代码是坏的。Node.js是单进程单线程启动的,所有业务代码都在主线程上工作,会造成CPU持续占用,阻塞主线程,也是CPU资源的浪费,远非如此真正的线程休眠。conststart=newDate();while(newDate()-start<2000){}运行后,如上图,CPU会暴涨,同时事件循环调度会被破坏,导致其他任务无法执行。2:Timer+Promise通过定时器延时执行函数setTimeout+Promise链依赖实现实现sleep,本质是新建一个Promise对象,然后在定时器延时时间到后执行resolve函数,这里Node.js执行线程不休眠,事件循环和V8正常运行。不过这也是目前比较普遍的解决办法,因为不能让主线程阻塞,否则程序就没法继续工作了。constsleep=ms=>newPromise(resolve=>setTimeout(resolve,ms));在Node.js中,也可以使用util模块提供的promisify方法实现,快捷方式。const{promisify}=require('util');constsleep=promisify(setTimeout);因为是基于timer和Promise,自然是异步的,所以使用的时候要注意,如下图://asyncawaitmethodasyncfunctiontest(){console.log(1);awaitsleep(3000);console.log(2);}//Promise链调用方法asyncfunctiontest(){console.log(1);sleep(3000).then(()=>{console.log(2);});}第三:零CPU开销,真正的事件循环防止睡眠实现ECMA262草案提供了Atomics.waitAPI来实现线程睡眠,它会真正阻??塞事件循环,阻塞线程直到超时。Atomics.wait(Int32Array,index,value[,timeout])方法将验证给定的Int32Array数组位置是否仍然包含它的值,它会等待唤醒或直到睡眠状态超时,并返回一个字符串指示是否超时或未唤醒。同样的,因为我们的业务是工作在主线程上的,所以避免在主线程中使用,根据实际需要在Node.js工作线程中使用。/***真正阻塞事件循环,阻塞线程直到超时,不要在主线程constvalid上使用*@param{Number}msdelay*@returns{String}ok|not-equal|timed-out*/functionsleep(ms){=ms>0&&ms#include#includenapi_valuesleepFn(napi_envenv,napi_callback_infoinfo){napi_statusstatus;size_targc=1;napi_valueargv[1];status=napi_get_cb_info(env,信息,&argc,argv,NULL,NULL);assert(status==napi_ok);if(argc<1){napi_throw_type_error(env,NULL,"msisrequired");returnNULL;}napi_valuetypevalueType;napi_typeof(env,argv[0],&valueType);if(valueType!=napi_number){napi_throw_type_error(env,NULL,"msmustbeanumber");returnNULL;}int64_ts;napi_get_value_int64(env,argv[0],&s);sleep(s);returnNULL;}napi_valueinit(napi_envenv,napi_valueexports){napi_statusstatus;napi_property_descriptordescriptor={"sleep",0,sleepFn,0,0,0,napi_default,0};status=napi_define_properties(env,exports,1,&descriptor);断言(status==napi_ok);returnexports;}NAPI_MODULE(睡眠,初始化);经过一系列的编译,导入.node文件,直接使用。//app.jsconst{sleep}=require('./build/Release/sleep.node');睡觉(3);五:easy-sleep模块这是作者https://github.com/qufei1993/easy-sleep写的一个小模块,其实是对以上方法的整合,包括C插件的编写,用作如下://Installnpmstalleasy-sleep-S//Asyncsleepconst{sleep}=require('easy-sleep');awaitsleep(3000);//Threadsleepconst{Thread}=require('easy-sleep');Thread.sleep();总结由于JavaScript是单线程语言,所以我们通常在主线程上工作。如果我们真的让线程休眠之后,事件循环就会被阻塞,后续的程序就无法正常运行。大多数情况下,我们只是简单封装setTimeout函数来实现延时功能。browser/Node.js工作线程下,可以根据实际需要决定是否休眠工作线程。