Node.js 執行緒你理解的可能是錯的

Randal發表於2019-02-16
本文程式碼執行環境
系統:MacOS High Sierra
Node.js:10.3.0
複製程式碼

Node.js是單執行緒的,那麼Node.js啟動後執行緒數是1?

答案:Node.js啟動後執行緒數並不是1,以下面程式碼為例

const http = require('http');

http.createServer((req, res) => {
    res.end('hello');
}).listen(8000, () => {
  console.log('server is listening: ' + 8000);
});
複製程式碼

通過Mac實用工具 > 活動監視器可以檢視程式的執行緒數其實是6

Node.js 執行緒你理解的可能是錯的

Node.js啟動的執行緒數不為1,是因為執行緒池?

答案:執行緒數不為1,不是因為執行緒池,而是因為V8。Node.js啟動後會建立V8例項,V8例項是多執行緒的,V8中的執行緒有:

  • 主執行緒:獲取程式碼、編譯執行
  • 編譯執行緒:主執行緒執行的時候,可以優化程式碼
  • Profiler執行緒:記錄哪些方法耗時,為優化提供支援
  • 其他執行緒:用於垃圾回收清除工作,因為是多個執行緒,所以可以並行清除

Node.js執行緒池是預先建立好的?

答案:並不是,執行緒池中的執行緒是按需建立的。

const http = require('http');
const fs = require('fs');

http.createServer((req, res) => {
  fs.readFile('./c.js', () => {});
  res.end('hello');
}).listen(8000, () => {
  console.log('server is listening: ' + 8000);
});
複製程式碼

上面程式碼啟動後,執行緒數依然是6

Node.js 執行緒你理解的可能是錯的

通過ab模擬訪問後

ab -n1000 -c20 'http://192.168.76.101:8000/'
複製程式碼

Node.js 執行緒你理解的可能是錯的
執行緒數才變成了10。之所以為10,是因為執行緒池中執行緒的預設值是4。

非同步IO都要佔用執行緒池?

答案:並不是,網路IO不會佔用執行緒池

const http = require('http');

http.createServer((req, res) => {
  http.get('http://192.168.1.100');
  res.end('hello');
}).listen(8000, () => {
  console.log('server is listening: ' + 8000);
});
複製程式碼

上面這段程式碼,使用ab壓測

ab -n10000 -c20 'http://192.168.76.101:8000'
複製程式碼

無論多少次訪問都不會建立執行緒,執行緒數永遠為6。

檔案IO一定會佔用執行緒池?

答案:並不是,*Sync會阻塞主執行緒所以不會佔用執行緒池,另外fs.FSWatcher也不會佔用執行緒池。

雖然官方文件裡面提到了fs.FSWatcher,但是其實並不能直接呼叫,關於FSWatcher訪問 StackOverflow上有一個相關的提問stackoverflow.com/questions/3…

執行緒池只能用於非同步IO?

答案:並不是,除了一些IO密集操作外,Node.js對一些CPU密集的操作也會放到執行緒池裡面執行(Crypto、Zlib模組)

Node.js 執行緒你理解的可能是錯的

DNS查詢也有可能佔用執行緒池?

答案:是的,因為dns.lookup方法會涉及到讀取本地檔案(例如nsswitch.conf,resolv.conf 以及 /etc/hosts)。而dns.resolve方法就不會佔用執行緒池,這也是lookup和resolve的區別所在。

所以下面的程式碼,其實是會佔用執行緒池的,因為http.get在dns解析的時候預設使用的是lookup方法。

const http = require('http');

http.createServer((req, res) => {
  http.get('http://bj.meituan.com');
  res.end('hello');
}).listen(8000, () => {
  console.log('server is listening: ' + 8000);
});
複製程式碼

執行緒池只供Node.js核心模組內部使用?

答案:不是的,官方文件裡面有如下描述

You can use the built-in Node Worker Pool by developing a C++ addon. On older versions of Node, build your C++ addon using NAN, and on newer versions use N-API. node-webworker-threads offers a JavaScript-only way to access Node's Worker Pool.

執行緒池內執行緒數可以為0嗎,可以無限大嗎?

答案:不可以為0,也不是無限大的。通過UV_THREADPOOL_SIZE可以修改執行緒池的執行緒數(預設為4),執行緒數最大值為128,最小值為1。

主執行緒任何時候都不會阻塞嗎?

答案:不是的,主執行緒在特定的情況下是會阻塞的。Node.js的事件迴圈中有一個階段是poll,poll階段在特定情況下是會阻塞的。這就是下圖服務啟動起來沒有任何使用者請求的時候Event Loop執行一次的時間比有少量請求的時候還要長的原因。

Node.js 執行緒你理解的可能是錯的
圖片來源:https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c

參考資料

https://nodejs.org/en/docs/guides/dont-block-the-event-loop/ https://medium.freecodecamp.org/what-exactly-is-node-js-ae36e97449f5 https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810

相關文章