nodejs 非同步I/O和事件驅動
非同步IO(asynchronous I/O)阻塞I/O和 非阻塞I/O
阻塞I/O,就是當用戶發一個讀取文件描述符的操作的時候,進程就會被阻塞,直到要讀取的數據全部準備好返回給用戶,這時候進程才會解除block的狀態。
非阻塞I/O,就與上面的情況相反,用戶發起一個讀取文件描述符操作的時,函數立即返回,不作任何等待,進程繼續執行。但是程序如何知道要讀取的數據已經準備好了呢?最簡單的方法就是輪詢。
除此之外,還有一種叫做IO多路復用的模式,就是用一個阻塞函數同時監聽多個文件描述符,當其中有一個文件描述符準備好了,就馬上返回,在linux下,select,poll,epoll都提供了IO多路復用的功能。
同步I/O和 非同步I/O
那麼同步I/O和非同步I/O又有什麼區別么?是不是只要做到非阻塞IO就可以實現非同步I/O呢?
其實不然。
同步I/O(synchronous I/O)做I/O operation的時候會將process阻塞,所以阻塞I/O,非阻塞I/O,IO多路復用I/O都是同步I/O。
非同步I/O(asynchronous I/O)做I/O opertaion的時候將不會造成任何的阻塞。
非阻塞I/O都不阻塞了為什麼不是非同步I/O呢?其實當非阻塞I/O準備好數據以後還是要阻塞住進程去內核拿數據的。所以算不上非同步I/O。
這裡借一張圖(圖來自這裡)來說明他們之間的區別
事件驅動
事件驅動(event-driven)是nodejs中的第二大特性,就是通過監聽事件的狀態變化來做出相應的操作。比如讀取一個文件,文件讀取完畢,或者文件讀取錯誤,那麼就觸發對應的狀態,然後調用對應的回掉函數來進行處理。
線程驅動和事件驅動
線程驅動編程和事件驅動編程之間的區別:
線程驅動就是當收到一個請求的時候,將會為該請求開一個新的線程來處理請求。一般存在一個線程池,線程池中有空閑的線程,會從線程池中拿取線程來進行處理,如果線程池中沒有空閑的線程,新來的請求將會進入隊列排隊,直到線程池中空閑線程。
事件驅動就是當進來一個新的請求的時,請求將會被壓入隊列中,然後通過一個循環來檢測隊列中的事件狀態變化,如果檢測到有狀態變化的事件,那麼就執行該事件對應的處理代碼,一般都是回調函數。
對於事件驅動編程來說,如果某個時間的回調函數是計算密集型,或者是阻塞I/O,那麼這個回調函數將會阻塞後面所有事件回調函數的執行。這一點尤為重要。
nodejs的事件驅動和非同步I/O事件驅動模型
nodejs是單線程(single thread)運行的,通過一個事件循環(event-loop)來循環取出消息隊列(event-queue)中的消息進行處理,處理過程基本上就是去調用該消息對應的回調函數。消息隊列就是當一個事件狀態發生變化時,就將一個消息壓入隊列中。
nodejs的時間驅動模型一般要注意下面幾個點:
1.因為是單線程的,所以當順序執行js文件中的代碼的時候,事件循環是被暫停的。
2.當js文件執行完以後,事件循環開始運行,並從消息隊列中取出消息,開始執行回調函數
3.因為是單線程的,所以當回調函數被執行的時候,事件循環是被暫停的
4.當涉及到I/O操作的時候,nodejs會開一個獨立的線程來進行非同步I/O操作,操作結束以後將消息壓入消息隊列。
不多說,看例子
varfs =require("fs");vardebug =require("debug")("example1");
debug("begin");
fs.readFile("package.json","utf-8",function(err,data){
if(err)
debug(err);
else
debug("get file content");
});
setTimeout(function(){
debug("timeout2");
});
debug("end");//運行到這裡之前,事件循環是暫停的
1.同步執行debug("begin")
2.非同步調用fs.readFile(),此時會開一個新的線程去進行非同步I/O操作
3.非同步調用setTimeout(),馬上將超時信息壓入到消息隊列中
4.同步調用debug("end")
5.開啟事件循環,彈出消息隊列中的信息(目前是超時信息)
6.然後執行信息對應的回調函數(事件循環又被暫停)
7.回調函數執行結束後,開始事件循環(目前消息隊列中沒有任何東西,文件還沒讀完)
8.非同步I/O讀取文件完畢,將消息壓入消息隊列(消息中含有文件內容或者是出錯信息)
9.事件循環取得消息,執行回調
10.程序退出。
這裡借一張圖來說明nodejs的事件驅動模型
這裡最後要說的一點就是如何手動將一個函數推入隊列,nodejs為我們提供了幾個比較方便的方法:
setTimeout()
process.nextTick()
setImmediate()
非同步I/O
nodejs中的非同步I/O的操作是通過libuv這個庫來實現的,包含了window和linux下面的非同步I/O實現。


TAG:全球大搜羅 |