最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

nodejs中實(shí)現(xiàn)sleep功能實(shí)例

 更新時(shí)間:2015年03月24日 10:07:19   投稿:junjie  
這篇文章主要介紹了nodejs中實(shí)現(xiàn)sleep功能實(shí)例,本文講解了sleep功能的開發(fā)過程和使用效果及性能測試,需要的朋友可以參考下

nodejs最讓人不爽的就是其單線程特性,很多事情沒法做,對CPU密集型的場景,性能也不夠強(qiáng)勁。很長一段時(shí)間,我想在javascript語言框架下尋求一些解決方案,解決無法操作線程、性能差的問題。曾經(jīng)最讓我印象深刻的方案是fibers,不過fibers也好,其他方案也好,在線程操作上還是很別扭,太過依賴輔助線程,本末倒置;就fiber而言,javascript固有的低性能問題并不能解決;最別扭的是在javascript語言框架下,線程間的消息傳遞常常很受限制,經(jīng)常無法真正地共享對象。

nodejs的addon方式無疑是極好的,具有極強(qiáng)的靈活性、完備的功能和原生代碼的性能。簡單說就是讓nodejs直接調(diào)用c/c++模塊,是一種javascript和native的混合開發(fā)模式。好東西呀,為什么不用呢?addon應(yīng)該算是一個(gè)大話題,今天我也不想太深入說這個(gè),我自己的實(shí)踐也不是很多。那就實(shí)現(xiàn)一個(gè)sleep函數(shù),就當(dāng)是拋磚引玉吧。

sleep

為什么javascript實(shí)現(xiàn)不了真正的sleep?sleep方法是通過向操作系統(tǒng)內(nèi)核注冊一個(gè)信號,指定時(shí)間后發(fā)送喚醒信號,而線程本身則掛起。本質(zhì)上當(dāng)線程 sleep(1000) 代表告訴操作系統(tǒng):1000ms內(nèi)不要給我分配CPU時(shí)間。所以sleep能保證線程掛起時(shí)不再占用CPU資源。而javascript是單線程運(yùn)行,本身取消了線程的概念,自然沒有辦法將主線程掛起中斷。

也有人會(huì)嘗試用javascript方法要實(shí)現(xiàn)sleep,例如這樣:

復(fù)制代碼 代碼如下:

function sleep(sleepTime) {
    for(var start = +new Date; +new Date - start <= sleepTime; ) { }
}

這是采用空循環(huán)阻塞住主進(jìn)程的運(yùn)行來實(shí)現(xiàn)sleep,明顯跟真正的sleep相去甚遠(yuǎn)。

那么如果實(shí)現(xiàn)一個(gè)真正的sleep呢?

環(huán)境準(zhǔn)備

開發(fā)環(huán)境

之前我的一些博客已經(jīng)說過,這里從略:node.js+npm、python 2.7、visual studio/ x-code。

 編譯工具

編譯工具需要采用node-gyp,較新版本的nodejs自帶此庫,如果沒有自帶node-gyp,請執(zhí)行:

復(fù)制代碼 代碼如下:

npm install -g node-gyp

 gyp特性我沒有精力去研究,如果你比較熟悉gcc等其他編譯器,不排除gyp會(huì)有不兼容之處,而且編譯選項(xiàng)和開關(guān)也是不盡相同。建議針對nodejs重新編寫c++代碼,如果確實(shí)有模塊需要復(fù)用,可以考慮先用熟悉的gcc編譯成動(dòng)態(tài)鏈接庫,再編寫少量代碼來使用動(dòng)態(tài)鏈接庫,再把這部分代碼用gyp編譯出來供nodejs使用。

進(jìn)入項(xiàng)目文件夾,執(zhí)行 npm init 初始化項(xiàng)目。為了讓nodejs知道我們想制作addon,我們需要在package.json中添加:

復(fù)制代碼 代碼如下:

"gyp-file": true

如果使用過gcc,那么你一定記得makefile。類似的,gyp也是通過一個(gè)文件來描述編譯配置,這個(gè)文件為binding.gyp,它是一個(gè)我們非常熟悉的json文件。gyp不是我們探討的重點(diǎn),所以binding.gyp也不會(huì)深入探究,我們只關(guān)注最重要的一些配置項(xiàng)。以下是一份簡單但完整的binding.gyp文件示例:

復(fù)制代碼 代碼如下:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ],
      "include_dirs": [
        "<!(node -e \"require('nan')\")"
      ]
    }
  ]
}


就看看這里面涉及的三個(gè)配置項(xiàng):

1.target_name:表示輸出出來的模塊名。
2.sources:表示需要編譯的源代碼路徑,這是一個(gè)數(shù)組。
3.include_dirs:表示編譯過程中要用到的目錄,這些目錄中的頭文件可以在預(yù)編譯指令 #include 搜索到。在這里使用了一個(gè)比較特殊的寫法,沒有把路徑用字符串常量給出,而是運(yùn)行一個(gè)命令 node -e "require('nan')" ,nan庫后面再說,先看看這個(gè)命令輸出什么: node_modules\nan ,原來這句命令的意思是返回nan庫的路徑。

C++編碼

OK,既然已經(jīng)配置了源代碼是hello.cc,那就建立一個(gè)這樣的文件。有一個(gè)問題需要提前提醒大家,我們所寫的c++模塊最終是要被v8引擎使用,所以api、寫法等受到v8引擎的制約。而不同版本的nodejs其實(shí)采用的v8引擎的版本也不盡相同,這也就意味著很難用一套c++代碼滿足不同版本的nodejs(指編譯過程,編譯完成后跨版本應(yīng)該能夠使用,沒有驗(yàn)證過。github不能上傳二進(jìn)制類庫,所以github上開源會(huì)有麻煩。npm可以直接上傳二進(jìn)制類庫,跳過編譯步驟,所以問題相對較小)。

node 0.11及以上版本:

復(fù)制代碼 代碼如下:

#include <node.h>
#include <v8.h>

using namespace v8;

void SleepFunc(const v8::FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = Isolate::GetCurrent();
  HandleScope scope(isolate);
  double arg0 = args[0] -> NumberValue();
  Sleep(arg0);
}

void Init(Handle<Object> exports) {
  Isolate* isolate = Isolate::GetCurrent();
  exports->Set(String::NewFromUtf8(isolate, "sleep"),
      FunctionTemplate::New(isolate, SleepFunc)->GetFunction());
}

NODE_MODULE(hello, Init);

node 0.10及以下版本:

復(fù)制代碼 代碼如下:

#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> SleepFun(const Arguments& args) {
  HandleScope scope; 
  double arg0 = args[0] -> NumberValue();
  Sleep(arg0);
  return scope.Close(Undefined());
}

void Init(Handle<Object> exports) {
  exports->Set(String::NewSymbol("sleep"),
      FunctionTemplate::New(SleepFun)->GetFunction());
}

NODE_MODULE(hello, Init);


可以看出,變化還是相當(dāng)大的,如果能屏蔽這些差異就太好了,有辦法了?我寫這么多還不就是想告訴你有辦法。是時(shí)候請出nan庫了。

nan

還記得在binding.gyp中,我們引入nan庫的路徑,就是要在這里用。nan庫是干嘛的呢?它提供了一層抽象,屏蔽了nodejs 0.8、nodejs 0.10、nodejs 0.12、io.js之前addon的語法差異。贊!

先安裝: npm install --save nan ,看看同樣的功能,用了nan后如何實(shí)現(xiàn):

復(fù)制代碼 代碼如下:

#include <nan.h>
using namespace v8;

NAN_METHOD(Sleep){
    NanScope();
    double arg0=args[0]->NumberValue();
    Sleep(arg0);
    NanReturnUndefined();
}

void Init(Handle<Object> exports){
    exports->Set(NanSymbol("sleep"), FunctionTemplate::New(Sleep)->GetFunction());
}

NODE_MODULE(hello, Init);

你需要了解的就是nan這套東西,至于v8的那一套就可以不用關(guān)注。

從下往上看:

復(fù)制代碼 代碼如下:

NODE_MODULE(hello, Init);

 這句定義addon的入口。注意第一個(gè)參數(shù)要與我們在binding.gyp中target_name一項(xiàng)一致。第二個(gè)參數(shù)就是addon的入口函數(shù)。
 

復(fù)制代碼 代碼如下:

 void Init(Handle<Object> exports){
    exports->Set(NanSymbol("sleep"), FunctionTemplate::New(Sleep)->GetFunction());
}
 

 這段代碼就是addon的入口方法。它接收兩個(gè)參數(shù),分別是exports和module。上面的示例省略了第二個(gè)參數(shù)。如果模塊提供一個(gè)對象,可以像示例中那個(gè),直接給exports指定要提供的key-value;如果特殊一點(diǎn),僅提供一個(gè)數(shù)值,或一個(gè)函數(shù),則需要用到第二個(gè)參數(shù),類似于 NODE_SET_METHOD(module, "exports", foo); 。這個(gè)示例中是表示要輸出這樣一個(gè)模塊:

復(fù)制代碼 代碼如下:

{
    "sleep": Sleep
}

Sleep是一個(gè)函數(shù),下來就來看看Sleep的定義:

復(fù)制代碼 代碼如下:

NAN_METHOD(Sleep){
    NanScope();
    double arg0=args[0]->NumberValue();
    Sleep(arg0);
    NanReturnUndefined();
}

其實(shí)就是讀取javascript傳入的參數(shù),轉(zhuǎn)成double型,再調(diào)用c++的sleep方法。

編譯addon

下面就要開始編譯這個(gè)模塊了。首先執(zhí)行 node-gyp configure 來進(jìn)行構(gòu)建前準(zhǔn)備工作,它會(huì)生成一個(gè)build文件夾和一些文件。接下來運(yùn)行 node-gyp build 就可以開始編譯了。在這個(gè)示例中,最終會(huì)在/build/Release/目錄下生成一個(gè)hello.node文件,這就是最終能被javascript引用的addon模塊了。

如果后續(xù)對c++代碼有修改,就不用再運(yùn)行 node-gyp configure ,直接運(yùn)行 node-gyp build 就好。

nodejs使用

建立一個(gè)index.js,看看怎么用這個(gè)模塊吧:

復(fù)制代碼 代碼如下:

var sleep=require('./build/Release/hello.node').sleep;

console.log(new Date);
sleep(1000);
console.log(new Date);

// result
// Wed Mar 04 2015 14:55:18 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間)
// Wed Mar 04 2015 14:55:19 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間)       

很容易吧,跟普通的javascript函數(shù)的使用方式一模一樣。

至此本文想要分享的技術(shù)要點(diǎn)已經(jīng)闡述完了。不過……究竟跟開篇提供的方法比起來有什么不一樣?我不截圖了,直接說明結(jié)果:

由于addon方式采用的方法是線程掛起,理論上不會(huì)有CPU占用和內(nèi)存變化,結(jié)果也是驗(yàn)證了這一點(diǎn)。再看javascript循環(huán)模擬sleep的方式,因?yàn)橐恢痹谂苎h(huán),內(nèi)存增加一點(diǎn)可以理解,沒什么大不了;再看CPU占用25%,似乎還算過得去。真的是這樣嗎?揭露真相的時(shí)候到了。我測試的筆記本電腦的CPU是雙核四線程,再結(jié)合25%的CPU占用……難道雙核四線程中有一個(gè)線程就被這個(gè)sleep給占用了?其實(shí)我發(fā)現(xiàn)這期間并沒有一個(gè)線程被鎖死,不過這不是javascript的功勞,而是intel超線程的功勞。因?yàn)檎f是四線程,其實(shí)本質(zhì)是兩個(gè)處理核心只能是雙線程,只是cpu做了一個(gè)時(shí)間片切割上的小把戲。例如核心cpu01分成了t0和t2,假設(shè)在n tick(調(diào)度周期)后的一個(gè)tick內(nèi),任務(wù)會(huì)分到t0,那么在再后面一個(gè)tick,任務(wù)會(huì)分到t2。所以從一個(gè)比較長的時(shí)間尺度(相對于調(diào)度周期),一個(gè)任務(wù)在t0和t2上運(yùn)行的時(shí)間基本是相當(dāng)?shù)摹S谑浅尸F(xiàn)出來的情景是nodejs的進(jìn)程沒有占用t0或t2到100%,而是分別占用了50%上下。由于windows的進(jìn)程調(diào)度相對比較復(fù)雜,所以CPU占用量上下浮動(dòng)很大。可以這樣預(yù)測,如果是雙核雙線程的CPU來處理這個(gè)腳本,CPU占用會(huì)上升到50%,并且一個(gè)核心卡死。如果是單核CPU來處理,CPU一下子會(huì)上升到100%。

好像CPU這段說得有點(diǎn)多,超線程那些也是猜測,各位看看就好。

相關(guān)文章

  • Node.js?實(shí)現(xiàn)簡單爬蟲的示例代碼

    Node.js?實(shí)現(xiàn)簡單爬蟲的示例代碼

    本文主要介紹了Node.js?實(shí)現(xiàn)簡單爬蟲,爬取美食網(wǎng)站的菜品標(biāo)題和圖片鏈接,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • Nodejs全局安裝和本地安裝的不同之處

    Nodejs全局安裝和本地安裝的不同之處

    這篇文章主要介紹了Nodejs全局安裝和本地安裝的不同之處的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • 基于nodejs實(shí)現(xiàn)微信支付功能

    基于nodejs實(shí)現(xiàn)微信支付功能

    這篇文章主要為大家詳細(xì)介紹了基于nodejs實(shí)現(xiàn)微信支付功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • 修改node.js默認(rèn)的npm安裝目錄實(shí)例

    修改node.js默認(rèn)的npm安裝目錄實(shí)例

    今天小編就為大家分享一篇修改node.js默認(rèn)的npm安裝目錄實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-05-05
  • 教你如何用node連接redis的示例代碼

    教你如何用node連接redis的示例代碼

    這篇文章主要介紹了教你如何用node連接redis的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • 詳解Nodejs中自動(dòng)化瀏覽器操作神器Puppeteer的使用

    詳解Nodejs中自動(dòng)化瀏覽器操作神器Puppeteer的使用

    Puppeteer是一個(gè)JavaScript庫,它提供了一種方式來通過DevTools協(xié)議控制無頭瀏覽器,本文主要為大家介紹了Puppeteer的主要特性和使用方法,感興趣的可以了解下
    2024-01-01
  • 在Linux上用forever實(shí)現(xiàn)Node.js項(xiàng)目自啟動(dòng)

    在Linux上用forever實(shí)現(xiàn)Node.js項(xiàng)目自啟動(dòng)

    在一臺計(jì)算機(jī)上手動(dòng)跑Node項(xiàng)目簡單,node xx.js就搞定了,想讓Node項(xiàng)目后臺運(yùn)行,雖然不能直接用node命令搞定,但是在安裝了forever這個(gè)包以后,還是很輕松的。不過要是在遠(yuǎn)程服務(wù)器上構(gòu)建Node項(xiàng)目,如果沒法自啟動(dòng),一旦服務(wù)器重啟,那就麻煩了。
    2014-07-07
  • npm run dev和npm run serve的區(qū)別小結(jié)

    npm run dev和npm run serve的區(qū)別小結(jié)

    npm run serve和npm run dev是在開發(fā)階段使用npm運(yùn)行腳本的兩種常見命令,本文就來介紹一下這兩者的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • 輕松創(chuàng)建nodejs服務(wù)器(3):代碼模塊化

    輕松創(chuàng)建nodejs服務(wù)器(3):代碼模塊化

    這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(3):代碼模塊化,本文是對第一節(jié)的例子作了封裝,需要的朋友可以參考下
    2014-12-12
  • Linux環(huán)境部署node服務(wù)并啟動(dòng)詳細(xì)步驟

    Linux環(huán)境部署node服務(wù)并啟動(dòng)詳細(xì)步驟

    最近用node.js開發(fā)了一個(gè)web項(xiàng)目,下面這篇文章主要給大家介紹了關(guān)于Linux環(huán)境部署node服務(wù)并啟動(dòng)的詳細(xì)步驟,文中通過圖文以及示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05

最新評論

剑川县| 元氏县| 大港区| 汶上县| 美姑县| 兴仁县| 景泰县| 西盟| 丰县| 扬州市| 日照市| 长宁区| 高阳县| 客服| 承德市| 九龙城区| 商洛市| 辛集市| 黄陵县| 河曲县| 临沧市| 梁平县| 土默特右旗| 伊川县| 定州市| 永德县| 响水县| 拉孜县| 大宁县| 稻城县| 锡林浩特市| 桃园市| 江陵县| 深州市| 利津县| 盐山县| 长岛县| 都兰县| 洪洞县| 连城县| 会泽县|