併發-0-同步/異步/阻塞/非阻塞/進程/線程

同步、異步、阻塞、非阻塞,這四個概念不多人能夠說清楚,不信的話,你能夠先本身試着寫下來bash

同步和異步

關注維度:消息的通訊機制(synchronous communication/asynchronous communication)

判斷標準:調用者是否主動等待被調用者的返回結果

同步的理論說明:任務A的執行過程當中調用了任務B。任務A對任務B發起調用後,主動等待調用結果。

同步的生活舉例:你去書店問老闆,是否有《操做系統》這本書,老闆說:稍等,我查一下。而後開始查啊查,等查好了,告訴你結果(主動等待被調用方返回結果)。

異步的理論說明:任務A的執行過程當中調用了任務B。任務A對任務B發起調用後,繼續執行後續工做。任務B完成後經過狀態、通知來通知調用者。

異步的生活舉例:你去書店問老闆,是否有《操做系統》這本書,查好了打電話給你,而後直接掛電話了(此時被調用方不返回結果)。過了幾天,查好了,老闆主動打電話給你(被調用方回調調用方,告知結果)。
複製代碼

阻塞和非阻塞

關注維度:任務在等待調用結果時的狀態

判斷標準:調用方在等待被調用方的返回結果時,是否能夠作其餘事(是否被掛起)

阻塞的理論說明:任務A對任務B發起調用後,任務B須要執行一段時間纔可返回結果,任務A選擇等待任務B的返回結果(暫時掛起)。

阻塞的生活舉例:你去書店問老闆,是否有《操做系統》這本書,你會一直把本身掛起,什麼是後不幹,一直在那等,直到獲得返回結果。

非阻塞的理論說明:任務A對任務B發起調用後,與此同時,任務A在任務B執行的過程當中去完成別的工做,等待任務B結果返回後再繼續(不掛起,而是繼續執行本身的任務)。

非阻塞的生活舉例:你去書店問老闆,是否有《操做系統》這本書,無論老闆有沒有告訴你,你本身都先去玩了(繼續執行本身的任務而不是乾等),可是也要偶爾也要check一下老闆是否有告終果。
複製代碼

咱們把用戶線程當作調用者,把內核線程當作被調用者,用幾張圖和簡單的示例代碼描述一下當前流程的幾種I/O模型:併發

同步阻塞IO:

read(socket, buffer)
process(buffer)
複製代碼

同步非阻塞IO:

while(read(socket,buffer) != SUCCESS);
process(buffer);
複製代碼

IO多路複用:

select(socket);
while(1){
    sockets = select();
    for(socket in sockets){
        if(canRead(socket)){
            read(socket,buffer);
            process(buffer);
        }
    }
}
複製代碼

雖然這種方式容許在單個線程中處理多個IO請求,可是每一個IO請求的過程仍是阻塞的,平均時間甚至比同步阻塞IO模型要更長異步

Reactor模式:

和多路IO複用的差異在於,經過Reactor的方式,能夠將用戶線程輪詢IO操做狀態的工做交給Reactor的事件循環進行處理,用戶線程註冊事件處理器以後能夠繼續執行其餘的工做,Reactor線程負責調用內核的select函數檢查socket狀態

UserEventHandler handleEvent(){
    if(canRead(socket)){
        read(socket,buffer);
        process(buffer);
    }
}

Reactor.register(new UserEventHandler(socket));

Reactor.epollHandleEvents(){
    while(1){
        sockets = select();
        for(socket in sockets){
            getEventHandler(socket).handleEvent();
        }
    }
}
複製代碼

IO多路複用仍是使用了會阻塞線程的select系統調用,最多隻能算異步阻塞IO,而非真正的異步IOsocket

異步非阻塞IO:

在異步阻塞IO中,用戶線程收到通知後自行讀取數據、處理數據。而在異步非阻塞IO中,用戶線程收到通知時,數據已經被準備好,用戶線程能夠直接使用(省略了讀取數據這一過程)async

UserCompeletionHandler.handleEvent(buffer){ process(buffer); }

aioRead(socket, new UserCompeletionHandler());函數

進程是什麼:

計算機最初發明的初衷是用於解決耗時耗力的複雜計算,是一個計算器

最原始的計算機執行程序的過程以下:等待用戶輸入指令->用戶輸入->計算機操做->等待用戶輸入指令->用戶輸入->計算機操做。在用戶思考或者輸入的過程當中,計算機就空閒下來。

後來有了批處理系統,用戶能夠把許多指令(如輸入1,輸入2)寫在磁盤中,計算機的執行過程變爲:用戶輸入指令集合->取指令->執行->取指令->執行。        

批處理系統大大提升了便捷性,可是仍是存在一個問題,假設指令集合中有A,B兩個程序,當程序A進行I/O處理時,程序B只能等待程序A直到其運行完。也就是說,內存中只能有一個程序在執行。
複製代碼
|程序One|程序Two|    計算機中有兩個程序

Time1:     |內存|              內存中裝載程序One

Time2:             |內存|      內存中裝載程序Two

複製代碼
那麼,如何在內存中裝入多個程序呢?因而人們發明了進程,每一個在運行的程序都看作一個進程,給每一個進程分配合適大小的對應的內存地址空間,進程之間的空間互不干擾,而且保存每一個進程的運行狀態。經過進程之間的相互切換,使計算機看起來在一段時間內有幾個程序在同時執行。   
複製代碼
|程序One|程序Two|    計算機中有兩個程序

Time1:    |部份內存|部份內存|    內存中裝載程序One,Two,分別在不一樣的部分。

複製代碼
進程讓程序之間的併發成爲了可能,從宏觀上看,某個時間段內有多個程序在同時執行,但實際上在某一時刻只有一個程序(一部分的內存)會獲得CPU,進行執行。
複製代碼

線程是什麼:

進程讓程序之間的併發成爲了可能,咱們能夠在電腦上同時聽歌,打字了(兩個不一樣的程序之間切換)。
複製代碼

可是人們對程序實時性的要求愈來愈高。好比對QQ音樂來講,它不只要處理用戶所發送的交互請求,還要播放歌曲。假設某一時刻QQ音樂在播放歌曲,你點擊了「暫停」按鈕,須要等待播放歌曲完畢以後才能處理「暫停」操做,這種程序確定是不合格的。ui

因而人們把QQ音樂這個程序所對應的進程拆分紅了多個線程,有播放歌曲的線程,處理交互請求的線程,每一個線程負責一個獨立的子任務,這樣的話,咱們點擊了「暫停」按鈕,QQ音樂會釋放暫停播放歌曲的線程,讓交互請求的線程處理用戶的請求,響應完以後再切換回來,讓播放歌曲的線程獲得CPU。具體過程以下:spa

播放線程---------------------掛起`````````````````播放線程----------     

                  用戶點擊暫停

                                  交互線程--------處理完畢
複製代碼
線程讓進程內的子任務併發成爲了可能。
複製代碼

3.程序,進程,線程:操作系統

程序是咱們寫的代碼,須要對應到一個具體的進程來運行,進程間有獨立的內存地址,互不干擾。線程是進程的子任務,屬於同一進程的線程共享相同的內存。線程

進程讓程序之間的併發成爲了可能,線程讓進程內的子任務併發成爲了可能。

相關文章
相關標籤/搜索