瀏覽器多線程和js單線程

1、什麼是進程和線程

在涉及瀏覽器多線程和js單線程以前,咱們先鋪墊一下前置概念:javascript

一、進程(process)

進程和線程都是操做系統的概念。html

進程是應用程序的執行實例,每一個進程是由私有的虛擬地址空間、代碼、數據和其它各類系統資源組成,即進程是操做系統進行資源分配和獨立運行的最小單元。java

當咱們啓動一個應用,計算機會至少建立一個進程,cpu會爲進程分配一部份內存,應用的全部狀態都會保存在這塊內存中,應用也許還會建立多個線程來輔助工做,這些線程能夠共享這部份內存中的數據。若是應用關閉,進程會被終結,操做系統會釋放相關內存。ios

mac電腦能夠在活動監視器中查看啓動的進程數: web

活動監視器

二、線程(thread)

  • 進程內部的一個執行單元,是被系統獨立調度和分派的基本單位。系統建立好進程後,實際上就啓動執行了該進程的主執行線程ajax

  • 進程就像是一個有邊界的生產廠間,而線程就像是廠間內的一個個員工,能夠本身作本身的事情,也能夠相互配合作同一件事情,因此一個進程能夠建立多個線程。算法

  • 線程本身不須要系統從新分配資源,它與同屬一個進程的其它線程共享當前進程所擁有的所有資源。 PS: 進程之間是不共享資源和地址空間的,因此不會存在太多的安全問題,而因爲多個線程共享着相同的地址空間和資源,因此會存在線程之間有可能會惡意修改或者獲取非受權數據等複雜的安全問題。json

而如今通用叫法單線程與多線程,都是指在一個進程內的單和多。axios

若是對進程及線程的理解還存在疑惑,能夠參考下述文章👇 www.ruanyifeng.com/blog/2013/0…瀏覽器

關於單核處理器、多核處理器、多處理器是怎麼處理進程和線程的,能夠參考下述文章👇 blog.csdn.net/alinshen/ar… jsonliangyoujun.iteye.com/blog/235827…

2、瀏覽器的多進程

其實若是要開發一個瀏覽器,它能夠是單進程多線程的應用,也能夠是使用 IPC 通訊的多進程應用。

不一樣瀏覽器採用了不一樣的架構模式,這裏我們只研究以Chrome爲表明的瀏覽器:

Chrome 採用多進程架構

Chrome 的不一樣進程

每打開一個tab頁,就至關於於建立了一個獨立的瀏覽器進程,這一點從上面的圖中能夠看出,可是也不是絕對的,它也有本身的優化機制,有的進程可能會被合併。

一、Chrome 的主要進程及其職責

  • Browser Process 瀏覽器的主進程(負責協調、主控) (1)負責包括地址欄,書籤欄,前進後退按鈕等部分的工做 (2)負責處理瀏覽器的一些不可見的底層操做,好比網絡請求和文件訪問 (3)負責各個頁面的管理,建立和銷燬其餘進程
  • Renderer Process 負責一個 tab 內關於網頁呈現的全部事情,頁面渲染,腳本執行,事件處理等
  • Plugin Process 負責控制一個網頁用到的全部插件,如 flash 每種類型的插件對應一個進程,僅當使用該插件時才建立
  • GPU Process 負責處理 GPU 相關的任務

不一樣進程負責的瀏覽器區域示意圖

Chrome 還爲咱們提供了「任務管理器」,供咱們方便的查看當前瀏覽器中運行的全部進程及每一個進程佔用的系統資源,雙擊還能夠查看更多類別信息。

經過「頁面右上角的三個點點點 — 更多工具 — 任務管理器」便可打開相關面板。

任務管理器

二、Chrome 多進程架構的優缺點

優勢: (1)某一渲染進程出問題不會影響其餘進程 (2)更爲安全,在系統層面上限定了不一樣進程的權限

缺點: (1)因爲不一樣進程間的內存不共享,不一樣進程的內存經常須要包含相同的內容。爲了節省內存,Chrome 限制了最多的進程數,最大進程數量由設備的內存和 CPU 能力決定,當達到這一限制時,新打開的 Tab 會共用以前同一個站點的渲染進程。

Chrome 把瀏覽器不一樣程序的功能看作服務,這些服務能夠方便的分割爲不一樣的進程或者合併爲一個進程。

以 Broswer Process 爲例,若是 Chrome 運行在強大的硬件上,它會分割不一樣的服務到不一樣的進程,這樣 Chrome 總體的運行會更加穩定,可是若是 Chrome 運行在資源貧瘠的設備上,這些服務又會合併到同一個進程中運行,這樣能夠節省內存。

3、瀏覽器的多線程

對咱們fe來說,最重要的是Renderer Process下的多線程,就是咱們常說的瀏覽器內核。

Chrome瀏覽器爲每一個tab頁面單獨啓用進程,所以每一個tab網頁都有由其獨立的渲染引擎實例

瀏覽器內核是多線程,在內核控制下各線程相互配合以保持同步,一個瀏覽器一般由如下常駐線程組成:

一、GUI 渲染線程

負責渲染瀏覽器界面HTML元素,當界面須要重繪(Repaint)或因爲某種操做引起迴流(reflow)時,該線程就會執行。在Javascript引擎運行腳本期間,GUI渲染線程都是處於掛起狀態的,也就是說被」凍結」了.

ps:重排和重繪的區別,有興趣的話,請看👇的連接 juejin.im/entry/582f1…

二、JavaScript引擎線程

JS內核,負責處理Javascript腳本程序。 一直等待着任務隊列中任務的到來,而後解析Javascript腳本,運行代碼。一個Tab頁(renderer進程)中不管何時都只有一個JS線程在運行JS程序。

ps: GUI渲染線程與JS引擎線程是互斥的,因此若是JS執行的時間過長,這樣就會形成頁面的渲染不連貫,致使頁面渲染加載阻塞。

三、定時觸發器線程

  • 定時器setInterval與setTimeout所在線程
  • 瀏覽器定時計數器並非由JavaScript引擎計數的 由於JavaScript引擎是單線程的, 若是處於阻塞線程狀態就會影響記計時的準確, 所以經過單獨線程來計時並觸發定時是更爲合理的方案。

四、事件觸發線程

  • 用來控制事件輪詢,JS引擎本身忙不過來,須要瀏覽器另開線程協助
  • 當JS引擎執行代碼塊如鼠標點擊、AJAX異步請求等,會將對應任務添加到事件觸發線程中
  • 當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理任務隊列的隊尾,等待JS引擎的處理
  • 因爲JS的單線程關係,因此這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閒時纔會去執行)

五、異步http請求線程

在XMLHttpRequest在鏈接後是經過瀏覽器新開一個線程請求, 將檢測到狀態變動時,若是設置有回調函數,異步線程就產生狀態變動事件放到 JavaScript引擎的處理隊列中等待處理。

let xhr = new XMLHttpRequest();   // 不兼容ie6及更低,建立ajax實例
  xhr.open('get',`json/banner.json?_${Math.random()}`); //打開請求:發送請求前的一些配置項,get有緩存,因此要加隨機數,post不用
  xhr.onreadystatechange = ()=>{
     // 事件監聽,ajax狀態改變事件,基於這個事件能夠獲取服務器返回的響應頭主體內容(響應頭先回來)
     // 從這步開始,當前ajax任務開始,若是是同步的,後續代碼不執行,要等到ajax狀態成功後再執行,若是是異步的,不會
      if(xhr.readyState === 4 && /^(2|3)\d{2}$/.test(xhr.status)){
          //readyState 請求狀態 // status 返回狀態
          let data = JSON.parse(xhr.responseText);  // 獲取響應主體的內容
      }
  };
  xhr.send(null); // 發送 ajax (括號中傳遞的內容就是請求主體的內容)
複製代碼

這是一個簡單的XMLHttpRequest請求的四個步驟,基於XMLHttpRequest的表明產品$.ajax,axios等

3、Js爲何要是單線程?

js的單線程和它的用途有關,做爲瀏覽器腳本語言,它主要是用來處理頁面中用戶的交互,以及操做DOM樹、CSS樣式樹來給用戶呈現一份動態而豐富的交互體驗和服務器邏輯的交互處理。

若是JavaScript是多線程的方式來操做這些UI DOM,則可能出現UI操做的衝突;

若是Javascript是多線程的話,在多線程的交互下,處於UI中的DOM節點就可能成爲一個臨界資源,假設存在兩個線程同時操做一個DOM,一個負責修改一個負責刪除,那麼這個時候就須要瀏覽器來裁決如何生效哪一個線程的執行結果。

固然咱們能夠經過鎖來解決上面的問題。但爲了不由於引入了鎖而帶來更大的複雜性,Javascript在最初就選擇了單線程執行

這也解釋了爲何GUI線程和JS引擎是互斥的。

當JS引擎執行時GUI線程會被掛起,GUI更新則會被保存在一個隊列中等到JS引擎線程空閒時當即被執行。

爲了多核CPU的計算能力,HTML5提出Web Worker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。

4、Js的單線程會帶來什麼問題?怎麼處理的?

當調用棧中的函數調用須要花費咱們很是多的時間,會發生什麼?

好比正在運行一個複雜的圖像轉換的算法

當調用棧有函數在執行,瀏覽器就不能作任何事了 —— 它被阻塞了。這意味着瀏覽器不能渲染頁面,不能運行任何其它的代碼,它就這樣被卡住了。那麼問題來了 —— 你的應用再也不高效和使人滿意了。

一旦你的瀏覽器開始在調用棧運行不少不少的任務,它就頗有可能會長時間得不到響應。在這一點上,大多數的瀏覽器會採起拋出錯誤的解決方案,詢問你是否要終止這個頁面:

但這會毀了你的用戶體驗

js開發的時候有一個很重要概念:

以後 的代碼並不必定會在 如今 的代碼執行以後執行。換句話說,在定義中不能 如今 馬上完成的任務將會異步執行,這意味着可能不會像你認爲的那樣發生上面所說的阻塞問題。

誰會告訴 JS 引擎去執行你的程序?事實上,JS 引擎不是單獨運行的 —— 它運行在一個宿主環境中,對於大多數開發者來講就是典型的瀏覽器和 Node.js。實際上,現在,JavaScript 被應用到了從機器人到燈泡的各類設備上。每一個設備都表明了一種不一樣類型的 JS 引擎的宿主環境。

全部的環境都有一個共同點,就是都擁有一個事件循環內置機制,它隨着時間的推移每次都去調用 JS 引擎去處理程序中多個塊的執行。

這意味着 JS 引擎只是任意的 JS 代碼按需執行的環境。是它周圍的環境來調度這些事件(JS 代碼執行)。,其餘周圍環境就是指的在同一進程下的不一樣的線程,好比事件觸發線程、定時器線程等

因此,好比當你的 JavaScript 程序發出了一個 Ajax 請求去服務器獲取數據,你在一個函數(回調)中寫了 「response」 代碼,而後 JS 引擎就會告訴宿主環境: 「嘿,我如今要暫停執行了,可是當你完成了這個網絡請求,而且獲取到數據的時候,請回來調用這個函數。」

而後瀏覽器設置對網絡響應的監聽,當它有東西返回給你的時候,它將會把回調函數插入到事件循環隊列裏而後執行。

瀏覽器種的EventLoop

若是你想詳細的瞭解這方面的內容,👇的連接: web.jobbole.com/95613/

相關文章
相關標籤/搜索