摘要:對于而言,單線程指的是它的執行線程是單線程。對于來說,單線程不僅不是劣勢,它對于降低編程復雜度還有很重要的作用,單線程避免了多線程編程模型多線程死鎖狀態同步等問題。單線程的應用是脆弱了,但群體的力量是強大的。
我們常聽說 JavaScript 是單線程的,那這個單線程是什么意思呢?單線程是否意味 JavaScript 存在性能缺陷呢?
在瀏覽器端,JavaScript 單線程指的是 JavaScript 的執行線程與 UI 渲染線程共用一個線程。對于 NodeJS 而言,單線程指的是它的 JavaScript 執行線程是單線程。雖然 JavaScript 只能單線程執行,但 JavaScript 引擎可不是,它能夠創建多個線程為主線程服務。Web Worker 已經得到大部分瀏覽器的支持,NodeJS 也擁有自己的線程池來處理 I/O 操作。無論前端還是后端,JavaScript 已經能夠利用多個線程來提升程序性能了。
NodeJS 單線程異步 I/O 模型NodeJS 是單線程異步 I/O 模型。換句話說,NodeJS 代碼執行占用一個線程,而代碼中的 I/O 操作則是交給其它線程執行,執行完畢后將結果交還給主線程。
對于 NodeJS 來說,單線程不僅不是劣勢,它對于降低編程復雜度還有很重要的作用,單線程避免了多線程編程模型多線程死鎖、狀態同步等問題。而異步 I/O 避免了單線程同步編程模型的阻塞問題,使 CPU 得到更充分的使用。
NodeJS 異步 I/O 模型的實現離不開 libuv 層,libuv 提供了一個線程池來執行 I/O 操作,執行完畢后再將結果返回給執行線程,因此 I/O 操作不會阻塞執行線程地繼續執行。libuv 是一個事件驅動的異步 I/O 庫,它是跨平臺的,在 *nix 平臺下,自行實現了線程池,在 windows 平臺采用了 IOCP,IOCP 內部仍是線程池原理,libuv 的線程池默認為 4 個線程。接下來我們在 Linux 環境下看一看 NodeJS 的多個線程。
查看 NodeJS 多線程首先,我們需要先編寫一個 js 腳本,寫入一個定時器使得腳本不會因為執行完畢而被關掉。
setInterval(function () {}, 1000)
node命令執行該腳本,開啟另一個窗口(或者把程序放后臺執行)來查看 NodeJS 進程下的線程情況。
$ ps -a PID TTY TIME CMD 16699 pts/2 00:00:00 node 16706 pts/0 00:00:00 ps $ ps -L -p 16699 PID LWP TTY TIME CMD 16699 16699 pts/2 00:00:00 node 16699 16700 pts/2 00:00:00 V8 WorkerThread 16699 16701 pts/2 00:00:00 V8 WorkerThread 16699 16702 pts/2 00:00:00 V8 WorkerThread 16699 16703 pts/2 00:00:00 V8 WorkerThread 16699 16704 pts/2 00:00:00 node
可以看到包括 V8 引擎的工作線程在內,已經開啟了 6 個線程(MAC OS 系統用ps -M -p
require("fs").readFile("test.js", function () {}) setInterval(function () {}, 1000)
重新啟動腳本,可以看到,啟動的 4 個新線程正是 libuv 線程池默認的 4 個線程。
$ ps -a PID TTY TIME CMD 16745 pts/2 00:00:00 node 16755 pts/0 00:00:00 ps $ ps -L -p 16745 PID LWP TTY TIME CMD 16745 16745 pts/2 00:00:00 node 16745 16746 pts/2 00:00:00 V8 WorkerThread 16745 16747 pts/2 00:00:00 V8 WorkerThread 16745 16748 pts/2 00:00:00 V8 WorkerThread 16745 16749 pts/2 00:00:00 V8 WorkerThread 16745 16750 pts/2 00:00:00 node 16745 16751 pts/2 00:00:00 node 16745 16752 pts/2 00:00:00 node 16745 16753 pts/2 00:00:00 node 16745 16754 pts/2 00:00:00 node
可以通過修改環境變量process.env.UV_THREADPOOL_SIZE(最大 128)使 NodeJS 支持更多地線程。
// js process.env.UV_THREADPOOL_SIZE = 64 require("fs").readFile("test.js", function () {}) setInterval(function () {}, 1000) // bash $ ps -a PID TTY TIME CMD 16782 pts/2 00:00:00 node 16852 pts/0 00:00:00 ps $ ps -L -p 16782 | wc -l 71
重新執行腳本,可以看到減去第一行和 6 個初始線程,有 64 個線程在為 NodeJS 的異步 I/O 服務。
高并發和高可用JavaScript 是單線程,但 JavaScript 引擎能夠創建多個線程來服務與主線程,而 NodeJS 的主線程就像一個調度員,它能夠將 I/O 操作,例如網絡請求,分發給其它線程進行處理,在通過事件機制將結果返回給主線程,因此,NodeJS 編寫的服務器能夠支持極大的并發量,這也是 NodeJS 的優勢所在。NodeJS 主線程不宜進行大量地計算,因為這會阻塞主線程的運行。所以一般來說,NodeJS 適合 I/O 密集型場景,不適合 CPU 密集型場景。
除了多線程的支持,NodeJS 還提供 child_process 和 cluster 接口允許用戶創建很多子進程來處理任務。單線程的 NodeJS 應用是脆弱了,但群體的力量是強大的。多進程、多線程的 NodeJS 才是服務器性能和穩定性的保證。
參考資料http://docs.libuv.org/en/latest/threadpool.html
《深入淺出 NodeJS》
https://nodejs.org/dist/latest-v8.x/docs/api/child_process.html
https://nodejs.org/dist/latest-v8.x/docs/api/cluster.html
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85022.html
摘要:標準庫中的所有方法都提供非阻塞的異步版本,并接受回調函數,某些方法還具有對應的阻塞方法,其名稱以結尾。比較代碼阻塞方法同步執行,非阻塞方法異步執行。 阻塞與非阻塞概述 此概述介紹了Node.js中阻塞與非阻塞調用之間的區別,此概述將引用事件循環和libuv,但不需要事先了解這些主題,假設讀者對JavaScript語言和Node.js回調模式有基本的了解。 I/O主要指與libuv支持的...
摘要:異步編程在傳統編程實踐中,大多數操作都是同步發生的。中的異步編程異步是一種輸入輸出處理的形式,它允許在傳輸完成之前,其它處理能繼續進行。 本文轉載自:眾成翻譯譯者:網絡埋伏紀事鏈接:http://www.zcfy.cc/article/1759原文:https://blog.risingstack.com/node-hero-async-programming-in-node-js/ ...
摘要:單線程異步非阻塞然后,這又牽扯到了事件循環消息隊列,還有微任務宏任務這些。此步的位置不確定某個時刻后,定時器觸發線程通知事件觸發線程,事件觸發線程將回調函數加入消息隊列隊尾,等待引擎線程執行。 前言 Philip Roberts 在演講 great talk at JSConf on the event loop 中說:要是用一句話來形容 JavaScript,我可能會這樣: Java...
摘要:一異步編程原理顯然,上面這種方式和銀行取號等待有些類似,只不過銀行取號我們并不知道上一個人需要多久才會完成。下面來探討下中的異步編程原理。 眾所周知,JavaScript 的執行環境是單線程的,所謂的單線程就是一次只能完成一個任務,其任務的調度方式就是排隊,這就和火車站洗手間門口的等待一樣,前面的那個人沒有搞定,你就只能站在后面排隊等著。在事件隊列中加一個延時,這樣的問題便可以得到緩解...
閱讀 3153·2021-11-22 13:54
閱讀 3441·2021-11-15 11:37
閱讀 3606·2021-10-14 09:43
閱讀 3502·2021-09-09 11:52
閱讀 3599·2019-08-30 15:53
閱讀 2462·2019-08-30 13:50
閱讀 2060·2019-08-30 11:07
閱讀 891·2019-08-29 16:32