關於協程:nodejs和golang協程的不一樣

nodejs和golang都是支持協程的,從表現上來看,nodejs對於協程的支持在於async/await,golang對協程的支持在於goroutine。關於協程的話題,簡單來講,能夠看做是非搶佔式的輕量級線程。前端

 

 

協程自己

一句話歸納,上面提到了node

     "能夠看做是非搶佔式的輕量級線程"。c++

     在多線程中,把一段代碼放在一個線程中執行,cpu會自動將代碼分紅碎片,並在必定時間切換cpu控制權,線程經過鎖機制確保本身使用的資源在cpu執行別的線程的代碼時被修改(佔用的內存堆棧、硬盤數據資源等),也就是說經過鎖機制,golang

線程a在一塊內存中建立了一個變量,線程a代碼還沒結束,cpu切換去執行線程b了,可是因爲鎖,線程b沒法使用這塊內存。express

     若是僅在單核單線程cpu下來看,多線程和多協程沒有任何區別,由於線程不能並行,只能是cpu分碎片執行。c#

     協程就是相似這個意思。協程是線程內的東西(暫且不談多線程下的協程),當協程遇到阻塞時,就切換線程控制權,讓線程去執行另一個協程,只不過這個過程是排隊的。這和nodejs的事件輪詢是一回事,在nodejs中先告知系統我如今要promise

讀取文件了,系統讀取,io阻塞了,nodejs去執行下一段代碼B,執行完後檢查阻塞是否等待完畢,若是等待完畢就把結果推到事件隊列背後去執行回調函數。這一段話我用協程的意思來表達一下,把讀取文件,讀取結束後執行相應操做放在一個協程a服務器

內,執行代碼B放在一個協程b內,線程執行協程a,a遇到io阻塞了,切換線程控制權,執行協程b,b執行結束,切換線程控制權,執行協程a。對於js用戶來講,協程是回調的另外一種表現形式。多線程

 

function sleep(ms){koa

  return new Promise((resolve,reject)=>setTimeout(

     ()=>resolve(),ms

  ))

}

 

(async function (){

  await sleep(3000)

  console.log("你好")

}())

 

(async function (){

  await sleep(3000)

  console.log("世界")

}())

 

能夠這麼看,執行async函數就是運行一段協程代碼,await關鍵字就是切換協程,在await後就去執行其餘協程的代碼了。

 

func deferPrint(str string){

  time.Sleep(time.Second*2)

  fmt.Println(str)

}

 

func main(){

  go deferPrint("你好")

  go deferPrint("世界")

  //若是主協程不阻塞,永遠不會切換

  time.Sleep(time.Seconds*2)

}

 

golang的go關鍵字就是將一段代碼放在一個協程裏,線程選擇協程運行,遇見阻塞就自動切換協程運行,但須要注意的是,golang不會由於一個協程運行結束就自動切換,必須是阻塞以後

 

 

 

核心區別

鎖機制

golang的協程是能夠帶鎖的 Lock.Mutex() Unlock(),nodejs是號稱永遠不會死鎖也根本沒有鎖這回事。

沒有鎖會致使的問題在於佔用的資源被輕易修改

好比讀取一個文件,若是該文件爲0kb我就寫一個字符串進去,若是大於0kb,我就不執行任何操做。協程中會有兩次阻塞,第一次是讀取該文件,判斷文件大小,第二次是寫入。假若有兩個函數簽名以下

async function getFileSize(filename) : number

async function writeFile(data,filename) :bool

async function exec(){

  size = await getFileSize("./test.txt");

  if(size==0){

    await writeFile("你好","./test.txt")

    console.log("ok!")

  }

}

若是我第一次判斷結束後另外一個協程裏執行了插入操做,那麼幾個函數的執行順序就會變成

A: getFileSize()檢查文件大小,協程阻塞,切換協程

B: 寫入文件,協程阻塞,切換協程

A: getFileSize()檢查文本大小結束,能夠插入,執行writeFile()

    可是此時隊列中還有一個B協程的插入操做會在A以前執行,A協程對此不知道,覺得文件仍是0kb

 

若是文件被上鎖了

A: getFileSize()檢查文件大小,協程阻塞,切換協程

B: 寫入文件,哦——協程A鎖住了這個文件,那我等他釋放把,切換協程

A: getFileSize()完成,插入操做

B: 哦——協程A還在佔用,那我接着等

A: 搞定了,釋放鎖,我已經沒有什麼要執行的了,把我從隊列裏刪掉吧

B: 協程A釋放了文件的鎖,如今我能夠寫入了

 

 

線程支持

golang之因此要支持鎖協程,我想是爲了多線程支持。golang中能夠啓用多個線程並行執行相同數量的協程。

nodejs受限於v8的isolate機制,只能跑在單線程中。全部代碼沒法並行執行,沒法處理計算密集型應用場景。

 

切換機制

nodejs使用await阻塞協程,手動切換線程控制權,node的協程是c++控制的,c++裏寫了這個函數能夠被推入事件隊列就可以用promise封裝成協程

golang在協程阻塞時自動切換協程,因此在寫golang的時候全部的代碼能夠都寫同步代碼,而後用go關鍵字去調用,golang的協程是本身規定的,全部

         函數在阻塞時都必須切換線程控制權

 

取返回值

nodejs中async函數是能直接返回值的

golang只能傳遞一個引用的channel

 

 

總的來講golang和nodejs應用場景不一樣。nodejs適合前端鼓搗,用plug/ejs配合express/koa2從服務器http請求數據後再填到模版引擎裏

golang相似與小c++,最大的亮點就是使用協程管理多線程

對語言來講,不該該選邊站,但仍是捧一波c#,除了只能在.net上運行其餘碾壓其餘全部對手

相關文章
相關標籤/搜索