最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js?與并發(fā)模型的詳細介紹

 更新時間:2022年07月19日 17:08:00   作者:??淚已無痕???  
這篇文章主要介紹了Node.js?與并發(fā)模型的詳細介紹,Node.js?現(xiàn)在已成為構(gòu)建高并發(fā)網(wǎng)絡(luò)應(yīng)用服務(wù)工具箱中的一員,文章圍繞主題展開詳細的內(nèi)容介紹,需要的朋友可以參考一下

前言:

Node.js 現(xiàn)在已成為構(gòu)建高并發(fā)網(wǎng)絡(luò)應(yīng)用服務(wù)工具箱中的一員,何以 Node.js 會成為大眾的寵兒?本文將從進程、線程、協(xié)程、I/O 模型這些基本概念說起,為大家全面介紹關(guān)于 Node.js 與并發(fā)模型的這些事。

進程

我們一般將某個程序正在運行的實例稱之為進程,它是操作系統(tǒng)進行資源分配和調(diào)度的一個基本單元,一般包含以下幾個部分:

  • 程序:即要執(zhí)行的代碼,用于描述進程要完成的功能;
  • 數(shù)據(jù)區(qū)域:進程處理的數(shù)據(jù)空間,包括數(shù)據(jù)、動態(tài)分配的內(nèi)存、處理函數(shù)的用戶棧、可修改的程序等信息;
  • 進程表項:為了實現(xiàn)進程模型,操作系統(tǒng)維護著一張稱為進程表的表格,每個進程占用一個進程表項(也叫進程控制塊),該表項包含了程序計數(shù)器、堆棧指針、內(nèi)存分配情況、所打開文件的狀態(tài)、調(diào)度信息等重要的進程狀態(tài)信息,從而保證進程掛起后,操作系統(tǒng)能夠正確地重新喚起該進程。

進程具有以下特征:

  • 動態(tài)性:進程的實質(zhì)是程序在多道程序系統(tǒng)中的一次執(zhí)行過程,進程是動態(tài)產(chǎn)生,動態(tài)消亡的;
  • 并發(fā)性:任何進程都可以同其他進程一起并發(fā)執(zhí)行;
  • 獨立性:進程是一個能獨立運行的基本單位,同時也是系統(tǒng)分配資源和調(diào)度的獨立單位;
  • 異步性:由于進程間的相互制約,使進程具有執(zhí)行的間斷性,即進程按各自獨立的、不可預(yù)知的速度向前推進。

需要注意的是,如果一個程序運行了兩遍,即便操作系統(tǒng)能夠使它們共享代碼(即只有一份代碼副本在內(nèi)存中),也不能改變正在運行的程序的兩個實例是兩個不同的進程的事實。

在進程的執(zhí)行過程中,由于中斷、CPU 調(diào)度等各種原因,進程會在下面幾個狀態(tài)中切換:

  • 運行態(tài):此刻進程正在運行,并占用了 CPU;
  • 就緒態(tài):此刻進程已準備就緒,隨時可以運行,但因為其它進程正在運行而被暫時停止;
  • 阻塞態(tài):此刻進程處于阻塞狀態(tài),除非某個外部事件(比如鍵盤輸入的數(shù)據(jù)已到達)發(fā)生,否則進程將不能運行。

通過上面的進程狀態(tài)切換圖可知,進程可以從運行態(tài)切換成就緒態(tài)和阻塞態(tài),但只有就緒態(tài)才能直接切換成運行態(tài),這是因為:

  • 從運行態(tài)切換成就緒態(tài)是由進程調(diào)度程序引起的,因為系統(tǒng)認為當(dāng)前進程已經(jīng)占用了過多的 CPU 時間,決定讓其它進程使用 CPU 時間;并且進程調(diào)度程序是操作系統(tǒng)的一部分,進程甚至感覺不到調(diào)度程序的存在;
  • 從運行態(tài)切換成阻塞態(tài)是由進程自身原因(比如等待用戶的鍵盤輸入)導(dǎo)致進程無法繼續(xù)執(zhí)行,只能掛起等待某個事件(比如鍵盤輸入的數(shù)據(jù)已到達)發(fā)生;當(dāng)相關(guān)事件發(fā)生時,進程先轉(zhuǎn)換為就緒態(tài),如果此時沒有其它進程運行,則立刻轉(zhuǎn)換為運行態(tài),否則進程將維持就緒態(tài),等待進程調(diào)度程序的調(diào)度。

線程

有些時候,我們需要使用線程來解決以下問題:

  • 隨著進程數(shù)量的增加,進程之間切換的成本將越來越大,CPU 的有效使用率也會越來越低,嚴重情況下可能造成系統(tǒng)假死等現(xiàn)象;
  • 每個進程都有自己獨立的內(nèi)存空間,且各個進程之間的內(nèi)存空間是相互隔離的,而某些任務(wù)之間可能需要共享一些數(shù)據(jù),多個進程之間的數(shù)據(jù)同步就過于繁瑣。

關(guān)于線程,我們需要知道以下幾點:

  • 線程是程序執(zhí)行中的一個單一順序控制流,是操作系統(tǒng)能夠進行運算調(diào)度的最小單位,它包含在進程之中,是進程中的實際運行單位;
  • 一個進程中可以包含多個線程,每個線程并行執(zhí)行不同的任務(wù);
  • 一個進程中的所有線程共享進程的內(nèi)存空間(包括代碼、數(shù)據(jù)、堆等)以及一些資源信息(比如打開的文件和系統(tǒng)信號);
  • 一個進程中的線程在其它進程中不可見。

了解了線程的基本特征,下面我們來聊一下常見的幾種線程類型。

內(nèi)核態(tài)線程

內(nèi)核態(tài)線程是直接由操作系統(tǒng)支持的線程,其主要特點如下:

  • 線程的創(chuàng)建、調(diào)度、同步、銷毀由系統(tǒng)內(nèi)核完成,但其開銷較為昂貴;
  • 內(nèi)核可將內(nèi)核態(tài)線程映射到各個處理器上,能夠輕松做到一個處理器核心對應(yīng)一個內(nèi)核線程,從而充分地競爭與利用 CPU 資源;
  • 僅能訪問內(nèi)核的代碼和數(shù)據(jù);
  • 資源同步與數(shù)據(jù)共享效率低于進程的資源同步與數(shù)據(jù)共享效率。

用戶態(tài)線程

用戶態(tài)線程是完全建立在用戶空間的線程,其主要特點如下:

  • 線程的創(chuàng)建、調(diào)度、同步、銷毀由用戶空間完成,其開銷非常低;
  • 由于用戶態(tài)線程由用戶空間維護,內(nèi)核根本感知不到用戶態(tài)線程的存在,因此內(nèi)核僅對其所屬的進程做調(diào)度及資源分配,而進程中線程的調(diào)度及資源分配由程序自行處理,這很可能造成一個用戶態(tài)線程被阻塞在系統(tǒng)調(diào)用中,則整個進程都將會阻塞的風(fēng)險;
  • 能夠訪問所屬進程的所有共享地址空間和系統(tǒng)資源;
  • 資源同步與數(shù)據(jù)共享效率較高。

輕量級進程(LWP)

輕量級進程(LWP)是建立在內(nèi)核之上并由內(nèi)核支持的用戶線程,其主要特點如下:

  • 用戶空間只能通過輕量級進程(LWP)來使用內(nèi)核線程,可看作是用戶態(tài)線程與內(nèi)核線程的橋接器,因此只有先支持內(nèi)核線程,才能有輕量級進程(LWP);
  • 大多數(shù)輕量級進程(LWP)的操作,都需要用戶態(tài)空間發(fā)起系統(tǒng)調(diào)用,此系統(tǒng)調(diào)用的代價相對較高(需要在用戶態(tài)與內(nèi)核態(tài)之間進行切換);
  • 每個輕量級進程(LWP)都需要與一個特定的內(nèi)核線程關(guān)聯(lián),因此:
    • 與內(nèi)核線程一樣,可在全系統(tǒng)范圍內(nèi)充分地競爭與利用 CPU 資源;
    • 每個輕量級進程(LWP)都是一個獨立的線程調(diào)度單元,這樣即使有一個輕量級進程(LWP)在系統(tǒng)調(diào)用中被阻塞,也不影響整個進程的執(zhí)行;
    • 輕量級進程(LWP)需要消耗內(nèi)核資源(主要指內(nèi)核線程的??臻g),這樣導(dǎo)致系統(tǒng)中不可能支持大量的輕量級進程(LWP);
  • 能夠訪問所屬進程的所有共享地址空間和系統(tǒng)資源。

小結(jié)

上文我們對常見的線程類型(內(nèi)核態(tài)線程、用戶態(tài)線程、輕量級進程)進行了簡單介紹,它們各自有各自的適用范圍,在實際的使用中可根據(jù)自己的需要自由地對其進行組合使用,比如常見的一對一、多對一、多對多等模型,由于篇幅限制,本文對此不做過多介紹,感興趣的同學(xué)可自行研究。

協(xié)程

協(xié)程(Coroutine),也叫纖程(Fiber),是一種建立在線程之上,由開發(fā)者自行管理執(zhí)行調(diào)度、狀態(tài)維護等行為的一種程序運行機制,其特點主要有:

  • 因執(zhí)行調(diào)度無需上下文切換,故具有良好的執(zhí)行效率;
  • 因運行在同一線程,故不存在線程通信中的同步問題;
  • 方便切換控制流,簡化編程模型。

在 JavaScript 中,我們經(jīng)常用到的 async/await 便是協(xié)程的一種實現(xiàn),

比如下面的例子:

function updateUserName(id, name) {
  const user = getUserById(id);
  user.updateName(name);
  return true;
}

async function updateUserNameAsync(id, name) {
  const user = await getUserById(id);
  await user.updateName(name);
  return true;
}

上例中,函數(shù) updateUserName 和 updateUserNameAsync 內(nèi)的邏輯執(zhí)行順序是:

  • 調(diào)用函數(shù) getUserById 并將其返回值賦給變量 user;
  • 調(diào)用 user 的 updateName 方法;
  • 返回 true 給調(diào)用者。

兩者的主要區(qū)別在于其實際運行過程中的狀態(tài)控制:

  • 在函數(shù) updateUserName 的執(zhí)行過程中,按照前文所述的邏輯順序依次執(zhí)行;
  • 在函數(shù) updateUserNameAsync 的執(zhí)行過程中,同樣按照前文所述的邏輯順序依次執(zhí)行,只不過在遇到 await 時,updateUserNameAsync 將會被掛起并保存掛起位置當(dāng)前的程序狀態(tài),直到 await 后面的程序片段返回后,才會再次喚醒 updateUserNameAsync 并恢復(fù)掛起前的程序狀態(tài),然后繼續(xù)執(zhí)行下一段程序。

通過上面的分析我們可以大膽猜測:協(xié)程要解決的并非是進程、線程要解決的程序并發(fā)問題,而是要解決處理異步任務(wù)時所遇到的問題(比如文件操作、網(wǎng)絡(luò)請求等);在 async/await 之前,我們只能通過回調(diào)函數(shù)來處理異步任務(wù),這很容易使我們陷入回調(diào)地獄,生產(chǎn)出一坨坨屎一般難以維護的代碼,通過協(xié)程,我們便可以實現(xiàn)異步代碼同步化的目的。

需要牢記的是:協(xié)程的核心能力是能夠?qū)⒛扯纬绦驋炱鸩⒕S護程序掛起位置的狀態(tài),并在未來某個時刻在掛起的位置恢復(fù),并繼續(xù)執(zhí)行掛起位置后的下一段程序。

I/O 模型

一個完整的 I/O 操作需要經(jīng)歷以下階段:

  • 用戶進(線)程通過系統(tǒng)調(diào)用向內(nèi)核發(fā)起 I/O 操作請求;
  • 內(nèi)核對 I/O 操作請求進行處理(分為準備階段和實際執(zhí)行階段),并將處理結(jié)果返回給用戶進(線)程。

我們可將 I/O 操作大致分為阻塞 I/O、非阻塞 I/O、同步 I/O、異步 I/O 四種類型,在討論這些類型之前,我們先熟悉下以下兩組概念(此處假設(shè)服務(wù) A 調(diào)用了服務(wù) B):

阻塞/非阻塞:

  • 如果 A 只有在接收到 B 的響應(yīng)之后才返回,那么該調(diào)用為阻塞調(diào)用;
  • 如果 A 調(diào)用 B 后立即返回(即無需等待 B 執(zhí)行完畢),那么該調(diào)用為非阻塞調(diào)用。

同步/異步:

  • 如果 B 只有在執(zhí)行完之后再通知 A,那么服務(wù) B 是同步的;
  • 如果 A 調(diào)用 B 后,B 立刻給 A 一個請求已接收的通知,然后在執(zhí)行完之后通過回調(diào)的方式將執(zhí)行結(jié)果通知給 A,那么服務(wù) B 就是異步的。

很多人經(jīng)常將阻塞/非阻塞同步/異步搞混淆,故需要特別注意:

  • 阻塞/非阻塞針對于服務(wù)的調(diào)用者而言;
  • 同步/異步針對于服務(wù)的被調(diào)用者而言。

了解了阻塞/非阻塞同步/異步,我們來看具體的 I/O 模型

阻塞 I/O

定義:用戶進(線)程發(fā)起 I/O 系統(tǒng)調(diào)用后,用戶進(線)程會被立即阻塞,直到整個 I/O 操作處理完畢并將結(jié)果返回給用戶進(線)程后,用戶進(線)程才能解除阻塞狀態(tài),繼續(xù)執(zhí)行后續(xù)操作。

特點:

  • 由于該模型會阻塞用戶進(線)程,因此該模型不占用 CPU 資源;
  • 在執(zhí)行 I/O 操作的時候,用戶進(線)程不能進行其它操作;
  • 該模型僅適用于并發(fā)量小的應(yīng)用,這是因為一個 I/O 請求就能阻塞進(線)程,所以為了能夠及時響應(yīng) I/O 請求,需要為每個請求分配一個進(線)程,這樣會造成巨大的資源占用,并且對于長連接請求來說,由于進(線)程資源長期得不到釋放,如果后續(xù)有新的請求,將會產(chǎn)生嚴重的性能瓶頸。

非阻塞 I/O

定義:

  • 用戶進(線)程發(fā)起 I/O 系統(tǒng)調(diào)用后,如果該 I/O 操作未準備就緒,該 I/O 調(diào)用將會返回一個錯誤,用戶進(線)程也無需等待,而是通過輪詢的方式來檢測該 I/O 操作是否就緒;
  • 操作就緒后,實際的 I/O 操作會阻塞用戶進(線)程直到執(zhí)行結(jié)果返回給用戶進(線)程。

特點:

  • 由于該模型需要用戶進(線)程不斷地詢問 I/O 操作就緒狀態(tài)(一般使用 while 循環(huán)),因此該模型需占用 CPU,消耗 CPU 資源;
  • 在 I/O 操作就緒前,用戶進(線)程不會阻塞,等到 I/O 操作就緒后,后續(xù)實際的 I/O 操作將阻塞用戶進(線)程;
  • 該模型僅適用于并發(fā)量小,且不需要及時響應(yīng)的應(yīng)用。

同(異)步 I/O

用戶進(線)程發(fā)起 I/O 系統(tǒng)調(diào)用后,如果該 I/O 調(diào)用會導(dǎo)致用戶進(線)程阻塞,那么該 I/O 調(diào)用便為同步 I/O,否則為 異步 I/O。

判斷 I/O 操作同步異步的標準是用戶進(線)程與 I/O 操作的通信機制,其中:

  • 同步情況下用戶進(線)程與 I/O 的交互是通過內(nèi)核緩沖區(qū)進行同步的,即內(nèi)核會將 I/O 操作的執(zhí)行結(jié)果同步到緩沖區(qū),然后再將緩沖區(qū)的數(shù)據(jù)復(fù)制到用戶進(線)程,這個過程會阻塞用戶進(線)程,直到 I/O 操作完成;
  • 異步情況下用戶進(線)程與 I/O 的交互是直接通過內(nèi)核進行同步的,即內(nèi)核會直接將 I/O 操作的執(zhí)行結(jié)果復(fù)制到用戶進(線)程,這個過程不會阻塞用戶進(線)程。

Node.js 的并發(fā)模型

Node.js 采用的是單線程、基于事件驅(qū)動的異步 I/O 模型,個人認為之所以選擇該模型的原因在于:

  • JavaScript 在 V8 下以單線程模式運行,為其實現(xiàn)多線程極其困難;
  • 絕大多數(shù)網(wǎng)絡(luò)應(yīng)用都是 I/O 密集型的,在保證高并發(fā)的情況下,如何合理、高效地管理多線程資源相對于單線程資源的管理更加復(fù)雜。

總之,本著簡單、高效的目的,Node.js 采用了單線程、基于事件驅(qū)動的異步 I/O 模型,并通過主線程的 EventLoop 和輔助的 Worker 線程來實現(xiàn)其模型:

  • Node.js 進程啟動后,Node.js 主線程會創(chuàng)建一個 EventLoop,EventLoop 的主要作用是注冊事件的回調(diào)函數(shù)并在未來的某個事件循環(huán)中執(zhí)行;
  • Worker 線程用來執(zhí)行具體的事件任務(wù)(在主線程之外的其它線程中以同步方式執(zhí)行),然后將執(zhí)行結(jié)果返回到主線程的 EventLoop 中,以便 EventLoop 執(zhí)行相關(guān)事件的回調(diào)函數(shù)。

需要注意的是,Node.js 并不適合執(zhí)行 CPU 密集型(即需要大量計算)任務(wù);這是因為 EventLoop 與 JavaScript 代碼(非異步事件任務(wù)代碼)運行在同一線程(即主線程),它們中任何一個如果運行時間過長,都可能導(dǎo)致主線程阻塞,如果應(yīng)用程序中包含大量需要長時間執(zhí)行的任務(wù),將會降低服務(wù)器的吞吐量,甚至可能導(dǎo)致服務(wù)器無法響應(yīng)。

總結(jié)

Node.js 是前端開發(fā)人員現(xiàn)在乃至未來不得不面對的技術(shù),然而大多數(shù)前端開發(fā)人員對 Node.js 的認知僅停留在表面,為了讓大家更好地理解 Node.js 的并發(fā)模型,本文先介紹了進程、線程、協(xié)程,接著介紹了不同的 I/O 模型,最后對 Node.js 的并發(fā)模型進行了簡單介紹。雖然介紹 Node.js 并發(fā)模型的篇幅不多,但筆者相信萬變不離其宗,掌握了相關(guān)基礎(chǔ),再深入理解 Node.js 的設(shè)計與實現(xiàn)必將事半功倍。

到此這篇關(guān)于Node.js 與并發(fā)模型的詳細介紹的文章就介紹到這了,更多相關(guān)Node.js 并發(fā)模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • node.js中的path.join方法使用說明

    node.js中的path.join方法使用說明

    這篇文章主要介紹了node.js中的path.join方法使用說明,本文介紹了path.join的方法說明、語法、使用實例和實現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • 淺談nodejs中創(chuàng)建cluster

    淺談nodejs中創(chuàng)建cluster

    集群是一組相互獨立的、通過高速網(wǎng)絡(luò)互聯(lián)的計算機,它們構(gòu)成了一個組,并以單一系統(tǒng)的模式加以管理。一個客戶與集群相互作用時,集群像是一個獨立的服務(wù)器。集群配置是用于提高可用性和可縮放性。本文將詳細介紹如何在nodejs中創(chuàng)建cluster。
    2021-06-06
  • express默認日志組件morgan的方法

    express默認日志組件morgan的方法

    morgan是express默認的日志中間件,這篇文章主要介紹了express默認日志組件morgan的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • Nodejs 中文分詞常用模塊用法分析

    Nodejs 中文分詞常用模塊用法分析

    這篇文章主要介紹了Nodejs 中文分詞常用模塊用法,結(jié)合具體案例形式分析了node.js常用分詞模塊的基本功能、用法、效率與相關(guān)使用特點,需要的朋友可以參考下
    2023-05-05
  • 使用nodejs下載風(fēng)景壁紙

    使用nodejs下載風(fēng)景壁紙

    本文主要介紹了使用nodejs下載風(fēng)景壁紙的方法。具有一定的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • node.js express cors解決跨域的示例代碼

    node.js express cors解決跨域的示例代碼

    在Web開發(fā)中,當(dāng)一個網(wǎng)頁的源與另一個網(wǎng)頁的源不同時,就發(fā)生了跨域,本文就來介紹一下node.js express cors解決跨域的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • node.js express中app.param的用法詳解

    node.js express中app.param的用法詳解

    express.js是nodejs的一個MVC開發(fā)框架,并且支持jade等多種模板。下面這篇文章主要給大家介紹了關(guān)于node.js express中app.param用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-07-07
  • 詳解nodejs微信公眾號開發(fā)——4.自動回復(fù)各種消息

    詳解nodejs微信公眾號開發(fā)——4.自動回復(fù)各種消息

    這篇文章主要介紹了詳解nodejs微信公眾號開發(fā)——4.自動回復(fù)各種消息,非常具有實用價值,需要的朋友可以參考下
    2017-04-04
  • node.js 中間件express-session使用詳解

    node.js 中間件express-session使用詳解

    這篇文章主要給大家介紹了node.js中間件express-session使用的相關(guān)資料,文中介紹的非常詳細,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-05-05
  • 寶塔部署nodejs項目的實戰(zhàn)步驟

    寶塔部署nodejs項目的實戰(zhàn)步驟

    前段時間部署node項目的時候出現(xiàn)了一點問題,所以想著給大家總結(jié)下,這篇文章主要給大家介紹了關(guān)于寶塔部署nodejs項目的實戰(zhàn)步驟,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2022-12-12

最新評論

泸西县| 华池县| 钟山县| 武宁县| 桦南县| 勐海县| 嘉兴市| 延边| 桃江县| 偏关县| 京山县| 镇巴县| 浮山县| 葫芦岛市| 马关县| 曲水县| 宜川县| 井陉县| 永年县| 莫力| 碌曲县| 上高县| 光山县| 安康市| 申扎县| 兴国县| 桦甸市| 甘谷县| 乌鲁木齐市| 什邡市| 彭泽县| 阿瓦提县| 中西区| 兴宁市| 张家界市| 麟游县| 松阳县| 久治县| 上蔡县| 冀州市| 嘉鱼县|