<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          瀏覽器是如何調(diào)度進(jìn)程和線程的?

          共 3629字,需瀏覽 8分鐘

           ·

          2020-10-16 13:42

          最近正值秋招,面試了很多前端同學(xué),感悟頗多,后面我也會(huì)在公眾號為大家分享下我作為面試官的一些心得,以及對于我經(jīng)常會(huì)問的一些問題的講解。

          今天我們來聊一下瀏覽器(以Chrome為例)對線程和進(jìn)程的調(diào)度,這個(gè)問題幾乎是我每次面試必問的。相信大家都看過很多面經(jīng)會(huì)講 JavaScript 的執(zhí)行機(jī)制,很多同學(xué)熱衷于去背這些面經(jīng),以至于連 JavaScript 是單線程的都不知道,就開始回答宏任務(wù)、微任務(wù)了... 這種我真的特別無語,是真的理解還是背出來的解題思路其實(shí)一看便知了。所以我建議大家無論是準(zhǔn)備面試還是平時(shí)積累知識,一定不要太浮躁,要從根本上理解這個(gè)問題,而不是去記這些解題思路。

          線程和進(jìn)程

          首先我們來回顧下線程和進(jìn)程的概念:

          • 進(jìn)程:CPU 進(jìn)行資源分配的基本單位
          • 線程:CPU 調(diào)度的最小單位

          這是進(jìn)程和線程最官方也是最常見的兩個(gè)定義,但是這兩個(gè)概念太抽象了,很難以理解。通俗一點(diǎn)講:進(jìn)程可以描述為一個(gè)應(yīng)用程序的執(zhí)行程序,線程則是進(jìn)程內(nèi)部用來執(zhí)行某個(gè)部分的程序。

          下面再引用一段知乎的高贊回答,我感覺非常有意思:

          做個(gè)簡單的比喻:進(jìn)程=火車,線程=車廂

          • 線程在進(jìn)程下行進(jìn)(單純的車廂無法運(yùn)行)
          • 一個(gè)進(jìn)程可以包含多個(gè)線程(一輛火車可以有多個(gè)車廂)
          • 不同進(jìn)程間數(shù)據(jù)很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點(diǎn)換乘)
          • 同一進(jìn)程下不同線程間數(shù)據(jù)很易共享(A車廂換到B車廂很容易)
          • 進(jìn)程要比線程消耗更多的計(jì)算機(jī)資源(采用多列火車相比多個(gè)車廂更耗資源)
          • 進(jìn)程間不會(huì)相互影響,一個(gè)線程掛掉將導(dǎo)致整個(gè)進(jìn)程掛掉(一列火車不會(huì)影響到另外一列火車,但是如果一列火車上中間的一節(jié)車廂著火了,將影響到所有車廂)
          • 進(jìn)程可以拓展到多機(jī),進(jìn)程最多適合多核(不同火車可以開在多個(gè)軌道上,同一火車的車廂不能在行進(jìn)的不同的軌道上)
          • 進(jìn)程使用的內(nèi)存地址可以上鎖,即一個(gè)線程使用某些共享內(nèi)存時(shí),其他線程必須等它結(jié)束,才能使用這一塊內(nèi)存。(比如火車上的洗手間)-"互斥鎖"
          • 進(jìn)程使用的內(nèi)存地址可以限定使用量(比如火車上的餐廳,最多只允許多少人進(jìn)入,如果滿了需要在門口等,等有人出來了才能進(jìn)去)-“信號量”

          應(yīng)用程序如何調(diào)度進(jìn)程和線程

          當(dāng)一個(gè)應(yīng)用程序啟動(dòng)時(shí),一個(gè)進(jìn)程就被創(chuàng)建了。應(yīng)用程序可能會(huì)創(chuàng)建一些線程幫助它完成某些工作,但這不是必須的。操作系統(tǒng)會(huì)劃分出一部分內(nèi)存給這個(gè)進(jìn)程,當(dāng)前應(yīng)用程序的所有狀態(tài)都將保存在這個(gè)私有的內(nèi)存空間中。

          當(dāng)你關(guān)閉應(yīng)用時(shí),進(jìn)程也就自動(dòng)蒸發(fā)掉了,操作系統(tǒng)會(huì)將先前被占用的內(nèi)存空間釋放掉。

          一個(gè)程序并不一定只有一個(gè)進(jìn)程,進(jìn)程可以讓操作系統(tǒng)再另起一個(gè)進(jìn)程去處理不同的任務(wù)。當(dāng)這種情況發(fā)生時(shí),新的進(jìn)程又將占據(jù)一塊內(nèi)存空間。當(dāng)兩個(gè)進(jìn)程需要通信時(shí),它們進(jìn)行進(jìn)程間通訊。

          許多應(yīng)用程序都被設(shè)計(jì)成以這種方式進(jìn)行工作,所以當(dāng)其中一個(gè)進(jìn)程掛掉時(shí),它可以在其他進(jìn)程仍然運(yùn)行的時(shí)候直接重啟。

          多進(jìn)程和多線程

          理解了上面的內(nèi)容,我們再來重新梳理多進(jìn)程和多線程的概念:

          • 多進(jìn)程:多進(jìn)程指的是在同一個(gè)時(shí)間里,同一個(gè)計(jì)算機(jī)系統(tǒng)中如果允許兩個(gè)或兩個(gè)以上的進(jìn)程處于運(yùn)行狀態(tài)。多進(jìn)程帶來的好處是明顯的,比如你可以聽歌的同時(shí),打開編輯器敲代碼,編輯器和聽歌軟件的進(jìn)程之間絲毫不會(huì)相互干擾。
          • 多線程是指程序中包含多個(gè)執(zhí)行流,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來執(zhí)行不同的任務(wù),也就是說允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程來完成各自的任務(wù)。

          Chrome 的多進(jìn)程架構(gòu)

          由于瀏覽器本身沒有統(tǒng)一的規(guī)范,不同的瀏覽器之間的架構(gòu)可能完全不同,在瀏覽器剛被設(shè)計(jì)出來的時(shí)候,那時(shí)的網(wǎng)頁非常的簡單,每個(gè)網(wǎng)頁的資源占有率是非常低的,因此一個(gè)進(jìn)程處理多個(gè)網(wǎng)頁時(shí)可行的。然后在今天,大量網(wǎng)頁變得日益復(fù)雜。把所有網(wǎng)頁都放進(jìn)一個(gè)進(jìn)程的瀏覽器面臨在健壯性,響應(yīng)速度,安全性方面的挑戰(zhàn),所以大部分現(xiàn)代瀏覽器都是多進(jìn)程的。

          從上面的圖我們可以很明顯的看出 Chrome 是一個(gè)多進(jìn)程的架構(gòu),我們打開一個(gè)瀏覽器時(shí)會(huì)啟動(dòng)多個(gè)不同的進(jìn)程協(xié)助瀏覽器將頁面為我們呈現(xiàn)出來:

          • 瀏覽器進(jìn)程
          • 插件進(jìn)程
          • GPU進(jìn)程
          • 渲染進(jìn)程

          瀏覽器進(jìn)程

          瀏覽器最核心的進(jìn)程,負(fù)責(zé)管理各個(gè)標(biāo)簽頁的創(chuàng)建和銷毀、頁面顯示和功能(前進(jìn),后退,收藏等)、網(wǎng)絡(luò)資源的管理,下載等。

          插件進(jìn)程

          負(fù)責(zé)每個(gè)第三方插件的使用,每個(gè)第三方插件使用時(shí)候都會(huì)創(chuàng)建一個(gè)對應(yīng)的進(jìn)程、這可以避免第三方插件crash影響整個(gè)瀏覽器、也方便使用沙盒模型隔離插件進(jìn)程,提高瀏覽器穩(wěn)定性。

          GPU進(jìn)程

          負(fù)責(zé)3D繪制和硬件加速

          渲染進(jìn)程

          瀏覽器會(huì)為每個(gè)窗口分配一個(gè)渲染進(jìn)程、也就是我們常說的瀏覽器內(nèi)核,這可以避免單個(gè) page crash 影響整個(gè)瀏覽器。

          瀏覽器內(nèi)核的多線程

          瀏覽器內(nèi)核就是瀏覽器渲染進(jìn)程,從接收下載文件后再到呈現(xiàn)整個(gè)頁面的過程,由瀏覽器渲染進(jìn)程負(fù)責(zé)。瀏覽器內(nèi)核是多線程的,在內(nèi)核控制下各線程相互配合以保持同步,一個(gè)瀏覽器通常由以下常駐線程組成:

          • GUI 渲染線程
          • 定時(shí)觸發(fā)器線程
          • 事件觸發(fā)線程
          • 異步http請求線程
          • JavaScript 引擎線程

          GUI渲染線程

          GUI 渲染線程負(fù)責(zé)渲染瀏覽器界面 HTML 元素,當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行。

          定時(shí)觸發(fā)器線程

          瀏覽器定時(shí)計(jì)數(shù)器并不是由 JavaScript 引擎計(jì)數(shù)的, 因?yàn)?JavaScript 引擎是單線程的, 如果處于阻塞線程狀態(tài)就會(huì)影響記計(jì)時(shí)的準(zhǔn)確, 因此通過單獨(dú)線程來計(jì)時(shí)并觸發(fā)定時(shí)是更為合理的方案。

          事件觸發(fā)線程

          當(dāng)一個(gè)事件被觸發(fā)時(shí)該線程會(huì)把事件添加到待處理隊(duì)列的隊(duì)尾,等待JS引擎的處理。這些事件可以是當(dāng)前執(zhí)行的代碼塊如定時(shí)任務(wù)、也可來自瀏覽器內(nèi)核的其他線程如鼠標(biāo)點(diǎn)擊、AJAX異步請求等,但由于JS的單線程關(guān)系所有這些事件都得排隊(duì)等待JS引擎處理。

          異步http請求線程

          在XMLHttpRequest在連接后是通過瀏覽器新開一個(gè)線程請求, 將檢測到狀態(tài)變更時(shí),如果設(shè)置有回調(diào)函數(shù),異步線程就產(chǎn)生狀態(tài)變更事件放到 JavaScript引擎的處理隊(duì)列中等待處理。

          Javascript引擎線程

          Javascript 引擎,也可以稱為JS內(nèi)核,主要負(fù)責(zé)處理 Javascript 腳本程序,例如V8引擎。Javascript 引擎線程理所當(dāng)然是負(fù)責(zé)解析 Javascript 腳本,運(yùn)行代碼。

          由于 JavaScript 是可操縱 DOM 的,如果在修改這些元素屬性同時(shí)渲染界面(即 JavaScript 線程和UI線程同時(shí)運(yùn)行),那么渲染線程前后獲得的元素?cái)?shù)據(jù)就可能不一致了。因此為了防止渲染出現(xiàn)不可預(yù)期的結(jié)果,瀏覽器設(shè)置 GUI 渲染線程與 JavaScript 引擎為互斥的關(guān)系,當(dāng) JavaScript 引擎執(zhí)行時(shí) GUI 線程會(huì)被掛起, GUI 更新會(huì)被保存在一個(gè)隊(duì)列中等到引擎線程空閑時(shí)立即被執(zhí)行。

          JavaScript 為何設(shè)計(jì)成單線程

          從上面我們了解到 JavaScript 的執(zhí)行是單線程的,也就是說,同一個(gè)時(shí)間只能做一件事。那么,為什么 JavaScript 不設(shè)計(jì)成多個(gè)線程呢?這樣不是效率更高?

          作為瀏覽器腳本語言, JavaScript 的主要用途是與用戶互動(dòng),以及操作DOM。這決定了它只能是單線程,否則會(huì)帶來很復(fù)雜的同步問題。比如,假定 JavaScript 同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè) DOM 節(jié)點(diǎn)上添加內(nèi)容,另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器應(yīng)該以哪個(gè)線程為準(zhǔn)?

          所以,為了避免復(fù)雜性,從一誕生, JavaScript 就是單線程,這已經(jīng)成了這門語言的核心特征,將來也不會(huì)改變。

          WebWorker 多線程?

          Web Worker為Web內(nèi)容在后臺線程中運(yùn)行腳本提供了一種簡單的方法。線程可以執(zhí)行任務(wù)而不干擾用戶界面

          那么既然 JavaScript 本身被設(shè)計(jì)為單線程,為何還會(huì)有像 WebWorker 這樣的多線程 API 呢?我們來看一下 WebWorker 的核心特點(diǎn)就明白了:

          • 創(chuàng)建 Worker 時(shí), JS 引擎向?yàn)g覽器申請開一個(gè)子線程(子線程是瀏覽器開的,完全受主線程控制,而且不能操作DOM)
          • JS 引擎線程與 worker 線程間通過特定的方式通信(postMessage API,需要通過序列化對象來與線程交互特定的數(shù)據(jù))

          所以 WebWorker 并不違背 JS引擎是單線程的 這一初衷,其主要用途是用來減輕cpu密集型計(jì)算類邏輯的負(fù)擔(dān)。

          最后

          好了,了解完以上知識,再去學(xué)習(xí) JavaScript 的執(zhí)行機(jī)制吧,這些知識會(huì)讓你更快深入的理解。



          推薦閱讀




          我的公眾號能帶來什么價(jià)值?(文末有送書規(guī)則,一定要看)

          每個(gè)前端工程師都應(yīng)該了解的圖片知識(長文建議收藏)

          為什么現(xiàn)在面試總是面試造火箭?

          瀏覽 83
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久热精品在线观看 | 激情五月天视频 | 美女操骚逼 | 狠狠撸综合网 | 亚洲第198页 |