首頁 > web前端 > js教程 > 主體

nodejs中使用多執行緒程式設計的方法實例_node.js

WBOY
發布: 2016-05-16 16:08:02
原創
2283 人瀏覽過

在以前的博文別說不可能,nodejs中實作sleep中,我向大家介紹了nodejs addon的用法。今天的主題還是addon,繼續挖掘c/c 的能力,彌補nodejs的弱點。

我曾多次提到nodejs的效能問題。其實就語言本身而言,nodejs的表現還是很高的,雖然不如大多部靜態語言,但差距也不大;相對其他動態語言而言,速度優勢非常明顯。但為什麼我們常常說nodejs不能勝任CPU密集型場景呢?因為由於其單執行緒特性,對於CPU密集型場景,它並不能充分利用CPU。計算機科學中有一個著名的Amdahl定律:

假設總工作量W,可以分解為兩個部分:只能串列計算的Ws和允許並行計算的Wp。那麼,在p個CPU並行運算的情況下,效能上能夠帶來speedup倍的提升。 Amdahl定律描述了並行能做到的和不能做到的。它是一種理想情況,實際情況會複雜得多。例如並發很可能會引起資源的爭奪,需要增加各種鎖,從而常常讓並行處於等待狀態;並發還會額外帶來操作系統對線程調度切換的時間開銷,增加Ws。不過,當一項任務中,Wp比Ws大得多,並且有多個CPU核心可供使用時,並行帶來的效能提升是相當可觀的。

好,回到nodejs上。我們設想一個計算場景:計算4000000內的質數數目。這個場景程式實現的時候,以除法運算為主,不涉及記憶體、物件等操作,理論上能夠確保讓nodejs以相對較快的速度運行,不會落後c太多,便於對比。

javascript尋找質數的方法已經在這篇部落格中提供了,直接抄過來:

複製程式碼 程式碼如下:

function zhishu_js(num) {
    if (num == 1) {
        return false;
    }
    if (num == 2) {
        return true;
    }
    for (var i = 2; i         if (num % i == 0) {
            return false;
        }
    }
    return true;
}

再寫一個c語言版本的:

複製程式碼 程式碼如下:

#include

bool zhishu(int num){
    if (num == 1) {
        return false;
    }
    if (num == 2) {
        return true;
    }
    for (int i = 2; i         if (num % i == 0) {
            return false;
        }
    }
    return true;
};

在nodejs中,我們用一個從1到4000000的循環來檢索質數;c語言中,我們設定若干個線程,定義count為4000000,每個線程做如下操作要:如果count大於0,則取出count的值,並計算是否為質數,同時將count減1。根據這個思路,javascript版本的很容易寫:

複製程式碼 程式碼如下:

var count = 0;

for (j = 1; j     if(zhishu(j)){
        count ;
    }
}


關鍵難點就是c語言的多執行緒程式設計。早期c/c 並沒有考慮並行運算的需求,所以標準函式庫中並沒有提供多執行緒支援。而不同的作業系統通常實作也是有差別的。為了避免這種麻煩,我們採用pthread來處理執行緒。

下載pthread最新版本。由於我對gyp不熟,link依賴lib搞了半天沒搞定,最後我的方式是,直接把pthread的源代碼放到了專案目錄下,並在binding.gyp中把pthread.c添加到源代碼列表中,在編譯專案的時候把pthread也編譯一次。修改後的binding.gyp是這樣的:

複製程式碼 程式碼如下:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc","pthreads/pthread.c" ],
      "include_dirs": [
        "         "pthreads"
      ],
      "libraries": ["Ws2_32.lib"]
    }
  ]
}

 當然了,我這種方法很麻煩,如果你們只添加pthread中lib和include目錄的引用,並且不出現依賴問題,那是最好的,就沒有必要用我的方法來做。

那麼接下來就進入C/C 多執行緒的一切了,定義一個執行緒處理函數:

複製程式碼 程式碼如下:

pthread_mutex_t lock;

void *thread_p(void *null){
    int num, x=0;
    do{
        pthread_mutex_lock(&lock);
        num=count--;
        pthread_mutex_unlock(&lock);
        if(num>0){
            if(zhishu(num))x ;
        }else{
            break;
        }
    }while(true);
    std::cout     pthread_exit(NULL);
        return null;
}

 在執行緒與執行緒之間,對於count這個變數是互相競爭的,我們需要確保同時只能有一個執行緒操作count變數。我們透過 pthread_mutex_t lock; 增加一個互斥鎖。當執行 pthread_mutex_lock(&lock); 時,執行緒檢查lock鎖的情況,如果已鎖定,則等待、重複檢查,阻塞後續程式碼運行;如果鎖已釋放,則鎖定,並執行後續程式碼。對應的, pthread_mutex_unlock(&lock); 就是解除鎖定狀態。

由於編譯器在編譯的同時,進行編譯最佳化,如果一個語句沒有明確做什麼事情,對其他語句的執行也沒有影響時,會被編譯器優化掉。在上面的程式碼中,我加入了統計質數數量的程式碼,如果不加的話,像這樣的程式碼:

複製程式碼 程式碼如下:

for (int j = 0; j     zhishu(j);
}

 是會直接被編譯器跳過的,實際上不會運作。

加入addon的寫法已經介紹過了,我們實作從javascript接收一個參數,表示執行緒數,然後在c中建立指定數量的執行緒完成質數檢索。完整程式碼:

複製程式碼 程式碼如下:

#include
#include
#include
#include“pthreadspthread.h”
#定義 MAX_THREAD 100
使用命名空間 v8;

int count=4000000;
pthread_t tid[MAX_THREAD];
pthread_mutex_t 鎖;

void *thread_p(void *null){
    int num, x=0;
    做{
        pthread_mutex_lock(&lock);
        num=計數--;
        pthread_mutex_unlock(&lock);
        if(num>0){
            if(zhishu(num))x ;
        }其他{
            休息;
        }
    } while(true);
    std::cout     pthread_exit(NULL);
    回空;
}

NAN_METHOD(知樹){
    NanScope();
    pthread_mutex_init(&lock,NULL);
    double arg0=args[0]->NumberValue();
    int c=0;
    for (int j = 0; j         pthread_create(&tid[j],NULL,thread_p,NULL);
    }
    for (int j = 0; j         pthread_join(tid[j],NULL);
    }
    NanReturnUndefine();
}

void Init(Handle 導出){
    Exports->Set(NanSymbol("zhishu"), FunctionTemplate::New(zhishu)->GetFunction());
}

NODE_MODULE(你好,初始化);

 phread_create 可以建立線程,預設是joinable的,這個時候子執行緒受制於主執行緒;phread_join阻塞住主線程,等待子執行緒加入,直到子執行緒退出。如果子執行緒已退出,則phread_join不會做任何事情。所以對所有的執行緒都執行thread_join,可以確保所有的執行緒退出後才會例主執行緒繼續進行。

完善一下nodejs腳本:

複製程式碼以下程式碼:

var zhishu_c=require('./build/Release/hello.node').zhishu;
function zhishu(num) {
    if (num == 1) {
        回傳錯誤;
    }
    if (num == 2) {
        返回真實;
    }
    for (var i = 2; i         if (num % i == 0) {
            回復錯誤;
        }
    }
    返回真實;
}

console.time("c");
    zhishu_c(100);
console.timeEnd("c");

console.time("js");
var count=0;
for (j = 1; j     if(zhishu(j)){
        計數;
    }
}
console.log(計數);
console.timeEnd("js");

 看一下測試結果:

 單執行緒時,雖然C/C 的運行速度是nodejs的181%,但這個成績我們認為在動態語言中,還是非常好的。雙執行緒時速度提升最明顯,那是因為我的電腦是雙核心四執行緒CPU,這個時候已經可能在使用兩個核心在進行處理。 4線程時速度達到最大,此時應該是雙核四線程能達到的極限,當線程再增加時,並不能再提升速度了。上述Amdahl定律中,p已達上限4。再增加線程,會增加作業系統進程調度的時間,增加鎖定的時間,儘管同時也能增加對CPU時間的競爭,但總體而言,Ws的增加更加明顯,效能是下降的。如果在一台空閒的機器上做這個實驗,數據應該會更好一點。

從這個實驗中,我們可以得出這樣的結論,對於CPU密集型的運算,交給靜態語言去做,效率會提高很多,如果計算中較多涉及內存、字串、數組、遞歸等操作(以後再驗證),效能提升更為驚人。同時,合理地利用多執行緒能有效地提高處理效率,但並不是執行緒越多越好,要根據機器的情況合理配置。

對於nodejs本身,的確是不擅長處理CPU密集的任務,但有了本文的經驗,我想,想克服這個障礙,並非什麼不可能的事情。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板