1. 程式人生 > >nodejs多執行緒,真正的非阻塞

nodejs多執行緒,真正的非阻塞

node從他推出至今,充滿讚美和飽受詬病的都是其單執行緒模型,所有的任務都在一個執行緒中完成(I/O等例外),優勢的地方自然是免去了頻繁切換執行緒的開銷,以及減少資源互搶的問題等等,但是當nodejs面對cpu密集型模型的時候就力不從心了。儘管node擁有非同步機制,可以把一些耗時演算法丟入eventloop等待下個事件迴圈再做,但是因為其任然是單執行緒模型,所以終究會造成阻塞。

先解釋一下兩個名詞,Fibers 和 Threads。
Fibers 又稱纖程,可以理解為協同程式,類似py和lua都有這樣的模型。使用Fibers可以避免對資源的互搶,減少cpu和記憶體的消耗,但是Fibers並不能夠真正的並行執行,同一時刻只有一個Fibers在執行,如果在其中一個Fibers中執行過多的cpu操作或者寫了個死迴圈,則整個主程式將卡死住。node中的非同步事件迴圈模型就有點象這個。

Threads 又稱執行緒,他可以在同一時刻並行的執行,他們共享主程序的記憶體,在其中某一時刻某一個threads鎖死了,是不會影響主執行緒以及其他執行緒的執行。但是為了實現這個模型,我們不得不消耗更多的記憶體和cpu為執行緒切換的開銷,同時也存在可能多個執行緒對同一記憶體單元進行讀寫而造成程式崩潰的問題。

很多讓node支援多執行緒的方法是使用c/c++的addon來實現,在需要進行cpu密集型計算的地方,把js程式碼改寫成c/c++程式碼,但是如果開發人員對c++不是很熟悉,一來開發效率會降低不少,二來也容易出bug,而且我們知道在addon中的c++程式碼除了編譯出錯外,是很難除錯的,畢竟沒有vs除錯c++程式碼方便。

令人振奮的訊息,我們為什麼不讓node也支援多執行緒模型呢?於是Jorge為我們開發出了一個讓node支援多執行緒模型的模組:threads_a_gogo
github地址:https://github.com/xk/node-threads-a-gogo

有了threads-a-gogo(以下簡稱TAGG)這個模組之後,我們可以讓node做更多的事情,我記得以前我看過一篇文章,說node只能應付i/o密集型場景,在cpu密集型場景將完敗給apache,因為apache是為每一個請求起一條執行緒的,所以在處理cpu密集型任務時一個執行緒的高強度計算不會很大程度的影響其他執行緒,類似的還有php的fastcgi,這也是很多拿node和php進行比較時,php的擁護者們一直提出的理論。

我們先來做一個簡單的測試,用我們suqian大大最喜歡的斐波那契陣列來看一下,加入了多執行緒的node有多麼的強悍:(測試機器為4CPU)
沒有使用TAGG的正常情況,非同步也幫不了我們應對cpu密集型任務

function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}var n=8function back(){if(!--n)return console.timeEnd('no thread');}
    console.time('no thread');

    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})
    process.nextTick(function(){
        console.log(fibo (40));
        back();})

    process.nextTick(function(){
        console.log(fibo (40));
        back();})

process.nextTick(function(){
    console.log(fibo (40));
    back();})
process.nextTick(function(){
    console.log(fibo (40));
    back();})
process.nextTick(function(){
    console.log(fibo (40));
    back();})

我們模擬了8個非同步的行為,測試用的node v0.8.16版本,所以 process.nextTick還是非同步方法。最後我們輸出結果為:

165580141165580141165580141165580141165580141165580141165580141165580141no thread:23346ms

接下來我們使用TAGG模組來測試同樣的執行8次斐波那契陣列計算,看看成績如何?

function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}
console.time('8 thread');var numThreads=8;//建立執行緒池,最大數為8var threadPool=require('threads_a_gogo').createPool(numThreads).all.eval(fibo);//為執行緒池註冊程式var i=8;var cb =function(err,data){//註冊執行緒執行完畢的回撥函式
    console.log(data);if(!--i){
        threadPool.destroy();
        console.timeEnd('8 thread');}}
threadPool.any.eval('fibo(40)', cb);//開始向執行緒池中執行fibo(40)這個任務

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

threadPool.any.eval('fibo(40)', cb);

最重的結果:

1655801411655801411655801411655801411655801411655801411655801411655801418 thread:9510ms

相比不使用多執行緒模型的node,使用了TAGG模組之後,我們在4CPU伺服器上的測試結果要快上一倍還不止。
到這裡我們看上去找到了一個比較完美的解決方案應對CPU密集型任務,但是可能有同學會說,我可以使用cluster來做相同的事情,下面我們來做一個使用cluster計算這些任務的情況:

var cluster =require('cluster');var numCPUs =8;function fibo (n){return n >1? fibo(n -1)+ fibo(n -2):1;}
console.time('8 cluster');if(cluster.isMaster){// Fork workers.for(var i =0; i < numCPUs; i++){
    cluster.fork();}var i =8;
  cluster.on('exit',function(worker, code, signal){if(!--i){
            console.timeEnd('8 cluster');
            process.exit(0);}});}else{
    console.log(fibo (40));
    process.exit(0);}

程式碼上的複雜程度比使用TAGG要高的多,而且如果是動態計算斐波那契陣列的結果,編碼將更加困難,需要在fork時掛上不同的引數,出錯的機率也更大。同時還有更重要的一個事情,如果是建立一個http伺服器,如果4個cluster都在計算fibo,那第5個請求node將無法處理,而是用TAGG則還是能夠正常處理的,所以cluster並不能解決單執行緒模型的cpu密集計算帶來的阻塞問題,我們看下測試結果:

1655801411655801411655801411655801411655801411655801411655801411655801418 cluster:11925ms

TAGG模組還有其他更多的功能,比如事件觸發,平滑退出,檢視執行緒工作狀態等等,總之TAGG模組給node注入了新的活力,讓node一直飽受詬病的處理cpu密集任務問題得到了一個妥善的解決,就算你不擅長c++程式碼,也能夠輕鬆編寫出多執行緒的真正的非阻塞node程式了。

相關推薦

nodejs執行真正阻塞

node從他推出至今,充滿讚美和飽受詬病的都是其單執行緒模型,所有的任務都在一個執行緒中完成(I/O等例外),優勢的地方自然是免去了頻繁切換執行緒的開銷,以及減少資源互搶的問題等等,但是當nodejs面對cpu密集型模型的時候就力不從心了。儘管node擁有非同步機制,可以把一些耗時演算法丟入eventlo

02-node.js 單執行‘ 非同步’阻塞io

1、基本概念     同步:多個任務順序執行     非同步:多個任務並排執行 2、node的併發實現原理     Node JS是單執行緒應用程式,但它通過事件和回撥概念,支援併發。 由於Node JS每一個API是非同步的,作為一個單獨的執行緒,它使用非同步函

基於Socket的執行和非同步阻塞模式程式設計

      剛開始接觸socket的程式設計的時候,遇到了很多的問題,費了很大勁搞懂。其實往往都是一些比較基本的知識,但是都是很重要的,只要對其熟練的掌握後,相信對基於網路的程式設計會有很大的提高,呵呵。       就拿基於C/S結構的例子來說,我們先看看伺服器和客戶端的流

Java Socket程式設計(阻塞執行NIO)

服務端:伺服器Server類public class Server implements Runnable { private int port; private volatile boolean stop; private Selector sele

c#執行操作測試(阻塞執行結束任務)

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Te

執行——原子、原子自旋鎖和互斥鎖

nonatomic:非原子屬性,執行緒不安全的,效率高 atomic:原子屬性,執行緒安全的,效率相對低。 原子屬性是一種單(執行緒)寫多(執行緒)讀的多執行緒技術,不過可能會出現髒資料 atomi

Python爬蟲之執行程序

前言 我們之前寫的爬蟲都是單個執行緒的?這怎麼夠?一旦一個地方卡到不動了,那不就永遠等待下去了?為此我們可以使用多執行緒或者多程序來處理。 首先宣告一點! 多執行緒和多程序是不一樣的!一個是 thread 庫,一個是 multiprocessing 庫。而多執行緒 thread 在 Pytho

執行訊號量的簡單使用 GCD

基本概念 關於iOS開發中,多執行緒基本的概念和基本使用,我在這裡就不在重複說了。但是為了照顧到有的同學可能還不是對基本的概念不熟悉,可以參考一下這篇文章併發其實很簡單 說說訊號量,併發數 如果你有計算機基礎,那麼下面這段話應該很簡單就能理解 訊號量就是一個資源計數器,對訊號

libcurl執行gzip共享DNS

http://hi.baidu.com/jjxiaoyan/item/e17b9ec3e31b93d4964452d8   libcurl是一個不錯的socket庫,而且又是開源的。如果僅僅是簡單的HTTP請求,那麼只需要幾行程式碼就能輕鬆實現。不過要用libcurl實現高效、高頻率的HTTP請

學習彙集地如果你擅長計算機組成原理執行設計模式jvm前端或者其他都可以

擅長jvm 多執行緒 設計模式 資料庫 前端 分散式什麼的一起學習共同進步。   目的是大家在自學新的領域的時候有地方可以探討求疑 比如在看到垃圾回收各種收集器中遇到執行緒方面知識的時候 學習設計模式分不清單例和享元的區別,只有書本經驗不知道如何應用到實際開發的時候 學習資料庫系統

python 執行 程序 協程

1. 介紹: threading用於提供執行緒相關的操作,執行緒是應用程式中工作的最小單元。python當前版本的多執行緒庫沒有實現優先順序、執行緒組,執行緒也不能被停止、暫停、恢復、中斷。 2. 1  執行緒執行函式 #!/bin/python #coding:utf8 import

FMDB使用事務執行加密等比較全面的用法。

一:簡單使用 既然FMBD是對資料庫的封裝,那基本功能應該包括 增、刪、改、查。 主要使用到的類為FMDatabase(資料庫)FMResultSet(查詢的結果)。 其中增、刪、改,三個方法都是使用FMDatabase類的executeUpdate方法也就是都算作資料的更新, 而查

執行併發並行

多執行緒的引入 什麼是執行緒: 執行緒是程式執行的一條路徑,一個程序中可以包含多條執行緒 多執行緒併發可以提高程式效率,同時完成多項工作 併發和並行的區別 並行就是兩個任務同時執行(需要多

執行生產者消費者模式經典講解簡單易懂2

本模式以一個經典練習為案例: 使用2種鎖機制實現生產者和消費者模式 要求 練習(生產者消費者模式): 自定義同步容器,容器容量上限為10。可以在多執行緒中應用,並保證資料執行緒安全。 使用synchronized同步及wait()和notifyAll() 實現生產者消費者模式 邏

Python 執行(全域性變數)資料共享threading.Lock() 互斥鎖

  demo.py(互斥鎖): import threading import time # 定義一個全域性變數 g_num = 0 def test1(num): global g_num # 全域性變數可以實現執行緒間資料共享。也可以通過傳參實現 fo

執行高併發的情況下操作redis當中的資料如何加鎖?

多個執行緒同時去操作Redis當中的資料,假如不加鎖的情況下,會出現資料重複的問題。假如需要每次都只有一條執行緒去操作Redis當中的資料,需要給操作加上鎖。     但是去網上一搜,網上給Redis加鎖的機制都是利用Redis的setnx自身的方法去加鎖,但是這樣

java最簡單粗暴講解執行還不趕緊上車!

這裡並沒有講什麼新東西,只是把多執行緒一些知識來個總結。大家懂得可以複習複習,還有些童鞋對多執行緒朦朧的可以拿這個做為入門~ 舉個栗子說明啥是多執行緒:玩遊戲,前面一堆怪,每個怪都是一個執行緒,你射了一槍,子彈飛出去了,這顆子彈也是一個執行緒。你開啟你的程序管理,看到你遊戲的後臺程序,這就是程序

flask 原始碼淺析(flask 如何處理請求(執行程序IO路複用))

之前有閱讀過tornado 底層的實現,tornado 為了解決C10K 問題(沒聽說過C10K問題的請檢視: http://www.360doc.com/content/13/0522/18/1542811_287328391.shtml),在Linux 平臺下是使用了epoll(pyth

CPU如何執行程序、執行他們之間的關係是怎樣的

  好文章分享,轉自:https://www.cnblogs.com/csfeng/p/8670704.html 當面臨這些問題的時候,有兩個關鍵詞無法繞開,那就是並行和併發。 首先,要先了解幾個概念:   1、程序是程式的一次執行。   2、程序是資源分配的基本單位(

java 執行執行

如果執行緒數量<=核心執行緒數量,那麼直接啟動一個核心執行緒來執行任務,不會放入佇列中。 如果執行緒數量>核心執行緒數,但<=最大執行緒數,並且任務佇列是LinkedBlockingDeque的時候,超過核心執行緒數量的任務會放在任務佇列中排隊。 如果執行緒數量>核心執行緒數,