您好,登錄后才能下訂單哦!
Node.js中怎么實(shí)現(xiàn)線程睡眠,針對這個(gè)問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
一:糟糕的 “循環(huán)空轉(zhuǎn)”
下面這段代碼是糟糕的,Node.js 是以單進(jìn)程、單線程的方式啟動(dòng),所有的業(yè)務(wù)代碼都工作在主線程,這樣會(huì)造成 CPU 持續(xù)占用,主線程阻塞對 CPU 資源也是一種浪費(fèi),與真正的線程睡眠相差甚遠(yuǎn)。
const start = new Date(); while (new Date() - start < 2000) {}
運(yùn)行之后如上圖所示,CPU 暴漲,同時(shí)也會(huì)破壞事件循環(huán)調(diào)度,導(dǎo)致其它任務(wù)無法執(zhí)行。
二:定時(shí)器 + Promise 實(shí)現(xiàn) sleep
通過定時(shí)器延遲執(zhí)行函數(shù) setTimeout + Promise 的鏈?zhǔn)揭蕾噷?shí)現(xiàn),本質(zhì)是創(chuàng)建一個(gè)新的 Promise 對象,待定時(shí)器延遲時(shí)間到了執(zhí)行 resolve 函數(shù)這時(shí) then 才會(huì)執(zhí)行,這里 Node.js 執(zhí)行線程是沒有進(jìn)行睡眠的,事件循環(huán)和 V8 等都是正常運(yùn)行的。但這也是目前通用的一種解決方案,因?yàn)槟悴荒茏屩骶€程阻塞,否則程序就無法繼續(xù)工作了。
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
在 Node.js 中還可以利用 util 模塊提供的 promisify 方法實(shí)現(xiàn),一種快捷方式。
const { promisify } = require('util'); const sleep = promisify(setTimeout);
因?yàn)槭腔诙〞r(shí)器與 Promise 所以也自然是異步的方式了,使用時(shí)也要注意,如下所示:
// async await 的方式 async function test() { console.log(1); await sleep(3000); console.log(2); } // Promise 的鏈?zhǔn)秸{(diào)用方式 async function test() { console.log(1); sleep(3000).then(() => { console.log(2); }); }
三:零 CPU 開銷真正的事件循環(huán)阻止 sleep 實(shí)現(xiàn)
ECMA262 草案提供了 Atomics.wait API 來實(shí)現(xiàn)線程睡眠,它會(huì)真正的阻塞事件循環(huán),阻塞線程直到超時(shí)。
該方法 Atomics.wait(Int32Array, index, value[, timeout]) 會(huì)驗(yàn)證給定的 Int32Array 數(shù)組位置中是否仍包含其值,在休眠狀態(tài)下會(huì)等待喚醒或直到超時(shí),返回一個(gè)字符串表示超時(shí)還是被喚醒。
同樣的因?yàn)槲覀兊臉I(yè)務(wù)是工作在主線程,避免在主線程中使用,在 Node.js 的工作線程中可以根據(jù)實(shí)際需要使用。
/** * 真正的阻塞事件循環(huán),阻塞線程直到超時(shí),不要在主線程上使用 * @param {Number} ms delay * @returns {String} ok|not-equal|timed-out */ function sleep(ms) { const valid = ms > 0 && ms < Infinity; if (valid === false) { if (typeof ms !== 'number' && typeof ms !== 'bigint') { throw TypeError('ms must be a number'); } throw RangeError('ms must be a number that is greater than 0 but less than Infinity'); } return Atomics.wait(int32, 0, 0, Number(ms)) } sleep(3000)
由于本節(jié)我們僅是在講解 sleep 的實(shí)現(xiàn),所以關(guān)于 Atomics.wait 方法睡眠之后如何被其它線程喚醒也不再此處講了,之后我會(huì)寫一講 Node.js 中的工作線程相關(guān)文章,到時(shí)會(huì)再次介紹。
四:基于 N-API 擴(kuò)展使用 C 語言實(shí)現(xiàn) sleep
通過 Addon 的方式使用 N-API 編寫 C/C++ 插件,借助其提供的系統(tǒng) sleep() 函數(shù)實(shí)現(xiàn)。
// sleep.c #include <assert.h> #include <unistd.h> #include <node_api.h> napi_value sleepFn(napi_env env, napi_callback_info info) { napi_status status; size_t argc = 1; napi_value argv[1]; status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL); assert(status == napi_ok); if (argc < 1) { napi_throw_type_error(env, NULL, "ms is required"); return NULL; } napi_valuetype valueType; napi_typeof(env, argv[0], &valueType); if (valueType != napi_number) { napi_throw_type_error(env, NULL, "ms must be a number"); return NULL; } int64_t s; napi_get_value_int64(env, argv[0], &s); sleep(s); return NULL; } napi_value init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor descriptor = { "sleep", 0, sleepFn, 0, 0, 0, napi_default, 0 }; status = napi_define_properties(env, exports, 1, &descriptor); assert(status == napi_ok); return exports; } NAPI_MODULE(sleep, init);
經(jīng)過一系列編譯之后,引入 .node 文件直接使用。
// app.js const { sleep } = require('./build/Release/sleep.node'); sleep(3);
五:easy-sleep 模塊
這是筆者寫的一個(gè)小模塊 https://github.com/qufei1993/easy-sleep,其實(shí)也是對以上幾種方法的整合,包含了 C 插件的編寫,使用如下:
// Install npm install easy-sleep -S // Async sleep const { sleep } = require('easy-sleep'); await sleep(3000); // Thread sleep const { Thread } = require('easy-sleep'); Thread.sleep();
關(guān)于Node.js中怎么實(shí)現(xiàn)線程睡眠問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。