進程和線程,同步和異步


進程與線程

一個程序中至少有一個進程,而一個進程中至少有一個線程

  • 進程是運行中的程序,線程是進程內部的一個執行序列
  • 進程是資源分配的單元,線程是執行單元
  • 進程間切換代價大,線程間切換代價小
  • 進程擁有的資源多,線程擁有的資源少
  • 多個線程共享進程的資源

如:

工廠的資源 -> 系統分配的內存(獨立的一塊內存)

工廠之間的相互獨立 -> 進程之間相互獨立

多個工人協作完成任務 -> 多個線程在進程中協作完成任務

工廠內有一個或多個工人 -> 一個進程由一個或多個線程組成

工人之間共享空間 -> 同一進程下的各個線程之間共享程序的內存空間(包括代碼段、數據集、堆等)

瀏覽器是多進程的!!!

瀏覽器的進程:

  • Browser進程:瀏覽器的主進程(負責協調、主控),只有一個
    • 負責瀏覽器界面顯示,與用戶交互。如前進后退
    • 負責各個頁面的管理,創建和銷毀其他進程
    • 將Renderer進程得到的內存中的Bitmap,會知道用戶界面上
    • 網絡資源的管理,下載等
  • 第三方插件進程:每種類型的插件對應一個進程,僅當使用插件時才創建
  • GPU進程:最多一個,用於3D繪制等
  • 瀏覽器渲染進程(瀏覽器內核)(Renderer進程內部是多線程的):默認每個Tab頁面一個進程,互不影響。
    • 頁面渲染、腳本執行、事件處理

重點:瀏覽器內核(渲染進程)

渲染進程是多線程的

包含:

  • GUI渲染線程
    • 負責渲染瀏覽器界面,解析HTML,CSS,構建DOM樹和RenderObject樹,布局和繪制等。
    • 當界面需要重繪(Repaint)或由於某種操作引發回流(reflow)時,該線程就會執行
    • 注意,GUI渲染線程與JS引擎線程是互斥的,當JS引擎執行時GUI線程會被掛起(相當於被凍結了),GUI更新會被保存在一個隊列中等到JS引擎空閑時立即被執行
  • JS引擎線程
    • 也稱為JS內核,負責處理Javascript腳本程序。(例如V8引擎)
    • JS引擎線程負責解析Javascript腳本,運行代碼。
    • JS引擎一直等待着任務隊列中任務的到來,然后加以處理,一個Tab頁(renderer進程)中無論什么時候都只有一個JS線程在運行JS程序
    • 同樣注意,GUI渲染線程與JS引擎線程是互斥的,所以如果JS執行的時間過長,這樣就會造成頁面的渲染不連貫,導致頁面渲染加載阻塞。
  • 事件觸發線程
    • 歸屬於瀏覽器而不是JS引擎,用來控制事件循環(可以理解,JS引擎自己都忙不過來,需要瀏覽器另開線程協助)
    • 當JS引擎執行代碼塊如setTimeOut時(也可來自瀏覽器內核的其他線程,如鼠標點擊、AJAX異步請求等),會將對應任務添加到事件線程中
    • 當對應的事件符合觸發條件被觸發時,該線程會把事件添加到待處理隊列的隊尾,等待JS引擎的處理
    • 注意,由於JS的單線程關系,所以這些待處理隊列中的事件都得排隊等待JS引擎處理(當JS引擎空閑時才會去執行)
  • 定時器觸發線程
    • 傳說中的setInterval與setTimeout所在線程
    • 瀏覽器定時計數器並不是由JavaScript引擎計數的,(因為JavaScript引擎是單線程的, 如果處於阻塞線程狀態就會影響記計時的准確)
    • 因此通過單獨線程來計時並觸發定時(計時完畢后,添加到事件隊列中,等待JS引擎空閑后執行)
    • 注意,W3C在HTML標准中規定,規定要求setTimeout中低於4ms的時間間隔算為4ms。
  • 異步http請求線程
    • 在XMLHttpRequest在連接后是通過瀏覽器新開一個線程請求
    • 將檢測到狀態變更時,如果設置有回調函數,異步線程就產生狀態變更事件,將這個回調再放入事件隊列中。再由JavaScript引擎執行。

單線程與多線程

單線程

所謂單線程(主線程),指在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個。

多線程

如瀏覽器的渲染進程:

  • GUI渲染線程
  • JS引擎線程
  • 事件觸發線程
  • 定時器觸發線程
  • 異步http請求線程

同步和異步

同步:如果在函數返回的時候,調用者就能夠得到預期結果(即拿到了預期的返回值或者看到了預期的效果),那么這個函數就是同步的

例如:

console.log('我是第一件事');
console.log('我是第二件事');

1.這兩個函數都是同步的

2.這段代碼是同步的

3.若有一個函數執行時間很長,后面的函數只能等待這個函數執行完才能執行(同步是阻塞的)

異步

先來看段代碼:

console.log('我是第一件事');
setTimeout(function () {
  console.log('我突然有事,晚點再做第二件事');
},1000)
console.log('我是第三件事');

//我是第一件事
//我是第三件事
//我突然有事,晚點再做第二件事

這段代碼實現的就是異步

1.執行console.log('我是第一件事')

2.setTimeout異步函數跳過

3.執行console.log('我是第三件事')

4.js引擎空閑1秒后將異步函數推入事件隊列-->執行console.log('我突然有事,晚點再做第二件事')

補充:setTimeout和setInterval接受兩個參數,第一個參數為函數。第二個為時間(毫秒),及js引擎空閑幾秒后將其推入事件隊列。

再來一個:

console.log(0,'第一');
for (let i = 0;i<3;i++){
	setTimeout(function(){
		console.log(i,'第三');
	},2000)
    console.log(i,'第二');
};

//0 "第一"
//0 "第二"
//1 "第二"
//2 "第二"
//兩秒之后
//0 "第三"
//1 "第三"
//2 "第三"

補充:

  • 異步機制是瀏覽器的兩個或兩個以上的常駐線程共同完成的
  • 如:異步請求是由兩個常駐線程:JS執行線程和事件觸發線程共同完成的,JS的執行線程發起異步請求(這時瀏覽器會開一條新的HTTP請求線程來執行請求,這時JS的任務已完成,繼續執行線程隊列中剩下的其他任務),然后在未來的某一時刻事件觸發線程監視到之前的發起的HTTP請求已完成,它就會把完成事件插入到JS執行隊列的尾部等待JS處理。
  • 如:定時觸發(settimeout和setinterval)是由瀏覽器的定時器線程執行的定時計數,然后在定時時間把定時處理函數的執行請求插入到JS執行隊列的尾端

消息隊列與事件循環

如上圖所示:

  • 左邊的棧存儲的是同步任務,就是那些能立即執行、不耗時的任務,如變量和函數的初始化、事件的綁定等等那些不需要回調函數的操作都可歸為這一類。

  • 右邊的堆用來存儲聲明的變量、對象。

  • 下面的隊列就是消息隊列,一旦某個異步任務有了響應就會被推入隊列中。如用戶的點擊事件、瀏覽器收到服務的響應和setTimeout中待執行的事件,每個異步任務都和回調函數相關聯。

  • JS引擎線程用來執行棧中的同步任務,當所有同步任務執行完畢后,棧被清空,然后讀取消息隊列中的一個待處理任務,並把相關回調函數壓入棧中,單線程開始執行新的同步任務。

  • JS引擎線程從消息隊列中讀取任務是不斷循環的,每次棧被清空后,都會在消息隊列中讀取新的任務,如果沒有新的任務,就會等待,直到有新的任務,這就叫事件循環。

以Ajax異步請求為例:

實例

最后來個大的:

setTimeout(function(){
    for(var i = 0; i < 100000000; i++){}
    console.log('timer a');
}, 0)

for(var j = 0; j < 5; j++){
    console.log(j);
}

setTimeout(function(){
    console.log('timer b');
}, 0)

function waitFiveSeconds(){
    var now = (new Date()).getTime();
    while(((new Date()).getTime() - now) < 5000){}
    console.log('finished waiting');
}

document.addEventListener('click', function(){
    console.log('click');
})

console.log('click begin');
waitFiveSeconds();

//0
//1
//2
//3
//4
//click begin
//finished waiting
//timer a
//timer b
//click

再說一下:setTimeout(fn,0);是js引擎空閑后立即插入隊列,並不是立即執行

1.第一個setTimeout異步函數跳過

2.執行for循環輸出0,1,2,3,4

3.第二個setTimeout異步函數跳過

4.執行console.log('click begin')輸出 click begin

5.調用waitFiveSeconds()函數輸出finished waiting

6.waitFiveSeconds()執行完后同步任務結束,js引擎空閑會一次將異步函數插入隊列

7.輸出timer a;timer b;click

參考文章1
參考文章2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM