1. 程式人生 > >深度理解nodejs[2]-事件迴圈

深度理解nodejs[2]-事件迴圈

程序與執行緒

我們在電腦中會執行多個程式,每一個程式中都會有多個執行緒。
例如我們執行比特幣客戶端的時候,我們某一個執行緒要處理網路、某一個執行緒要處理挖礦、某一個執行緒要處理使用者輸入…
執行緒的排程使用了作業系統級別的排程器來明確了哪一個執行緒應該被執行。執行緒也有優先順序之分,例如監聽滑鼠滑動的優先順序就會很高,因為其不能等待太長的時間。

為了在給定的時間內更快更多的處理執行緒:
1、我們可以通過增加CPU的核心數量或者是
2、排程器當監測到執行緒中執行中斷,如讀取檔案網路時,及時切換到其他的執行緒中執行。

事件迴圈

nodejs是單執行緒的事件迴圈機制
虛擬碼演示事件迴圈:

1

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const peningTimers =[];
const pendingOSTasks=[];
cosnt pendingOperations=[];

1、初始化
myfile.runContent()

function shouldContinue(){ //是否繼續
 1、檢查setTimeOut、setInterval、setImmediate
 2、檢查是否有監聽埠等作業系統級別的任務
 3、檢查是否有檔案、網路等長期的操作
return peningTimers.length || pendingOSTasks.length || pendingOperations.length;

}

2、事件迴圈
while(shouldContinue()){
//1.觀察peningTimers.length,是否調查setTimeOut、setInterval等函式
//2、觀察pendingOSTasks.length   pendingOperations.length,並呼叫相關回調函式
//3.暫停、一直等到上面的某一個事件完成
//4、呼叫setImmediate等函式
//5、處理close事件
}

3、退出

nodejs的單執行緒與多執行緒

nodejs的單執行緒,是對於其處理事件迴圈來講的,有了事件觸發,就會執行相應函式。沒有事件觸發,就會等待。從這個意義上來說,nodejs是單執行緒的。
但是在處理具體的任務,函式的時候。nodejs確是多執行緒的。

nodejs的單執行緒與多執行緒證明

1
2
3
4
5
6
7
const crypto = require('crypto');

const start = Date.now();

crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('1:',Date.now()-start);
});

測試pbkdf2速度:1: 868

1
2
3
4
5
6
7
8
9
10
11
const crypto = require('crypto');

const start = Date.now();

crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('1:',Date.now()-start);
});

crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('2:',Date.now()-start);
});

測試pbkdf2速度:

1
2
1: 891
2: 893

說明了pbkdf2函式是多執行緒來執行的。libuv中預設有4個執行緒,pbkdf2函式正是藉助libuv實現了多執行緒。

測試libuv中預設有4個執行緒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const crypto = require('crypto');

const start = Date.now();

crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('1:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('2:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('3:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('4:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('5:',Date.now()-start);
});
1
2
3
4
5
4: 919
1: 922
3: 936
2: 936
5: 1813

注意,明顯第5個執行緒時間增加了一倍,因為預設libuv中預設有4個執行緒,第5個執行緒陷入了等待。

修改libuv中預設預設執行緒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
process.env.UV_THREADPOOL_SIZE = 5;

const crypto = require('crypto');

const start = Date.now();

crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('1:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('2:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('3:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('4:',Date.now()-start);
});
crypto.pbkdf2('a','b',100000,512,'sha512',()=>{
 console.log('5:',Date.now()-start);
});

測試速度:

1
2
3
4
5
1: 956
5: 963
3: 970
2: 971
4: 974

http庫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const https = require('https');
const start = Date.now();

function dorequest(){
 https.request('https://www.baidu.com',res=>{
   res.on('data',()=>{});
   res.on('end',()=>{
     console.log(Date.now()-start);
   });
 })
 .end();
}
dorequest();
dorequest();
dorequest();
dorequest();
dorequest();
dorequest();
dorequest();
dorequest();
dorequest();

測試速度:

1
2
3
4
5
6
7
8
9
48
50
52
53
54
55
57
58
62

https網路訪問,呼叫了作業系統資源,libuv只是起到了代理的作用,所以不收到libuv預設4個執行緒的限制。

總結

pbkdf2等函式是藉助libuv實現多執行緒的。但是當這些函式執行完畢後,會觸發完成事件.nodejs主執行緒觸發事件的處理卻是單執行緒的。

image.png