鍍金池/ 問答/Java  PHP  C  C++  HTML/ Linux同步和異步-事件循環(huán)

Linux同步和異步-事件循環(huán)

請問下:

linux如何實現(xiàn),nginx是如何實現(xiàn)異步的?在哪個地方體現(xiàn)了
    1.進程同步和異步
    2.線程的同步和異步
    3.事件循環(huán)到底是什么意思?如何定義什么叫事件的?比如swoole說的事件驅(qū)動
我學了一年了,都沒搞明白。

如果說進程程序一直執(zhí)行叫同步,那么異步是如何實現(xiàn)的?
比如nginx內(nèi)部讀取圖片,是不是開一個線程或者進程,這樣和父進程沒有關(guān)系了就叫異步?
線程的同步,有相關(guān)函數(shù)通過信號來支持,比如鎖。那么異步是如何支持的?

希望大佬能用代碼 或者函數(shù)指定下,謝謝!
回答
編輯回答
我以為

說下自己的理解,供參考。假設(shè)題主了解網(wǎng)絡(luò)編程和計算機系統(tǒng)的一些基本概念。

簡單概括來說,事件驅(qū)動是實現(xiàn)并發(fā)處理的一種方式。

我們就以HTTP請求的處理過程為例,為簡化說明,僅考慮網(wǎng)絡(luò)IO,不考慮文件IO和數(shù)據(jù)庫等其他過程,也不考慮多核系統(tǒng)。
考慮采用如下最簡模型來處理HTTP請求:

main_loop:
  accept() 
  recv()  
  parse() 
  send() 
  close() 

來一個連接,讀取數(shù)據(jù)(請求),解析請求內(nèi)容,返回數(shù)據(jù)(應答)。
同一時間只為一個客戶端服務(wù)。在為A客戶端服務(wù)的過程中,B客戶端必須等待。

這種方式非常簡單直接,容易理解,但其無法滿足現(xiàn)實場景的需要——不支持并發(fā)。
現(xiàn)實中,客戶端的請求是并發(fā)的:即當一個客戶端的請求還在處理時,另外一個客戶端的請求就會達到,甚至多個客戶端的請求同時達到。
而且,recv 和 send等涉及網(wǎng)絡(luò)操作的API由于網(wǎng)絡(luò)數(shù)據(jù)發(fā)送與到達的不確定性,可能需要等待,CPU會空閑下來——但這種模型下即使CPU空閑了也無法處理其他客戶端的請求,浪費了CPU。

我們采用如下多線程模型,可以解決上述問題:

main_loop:
  accept() 
  start_thread(thread_loop)

thread_loop:    
    recv()  
    parse() 
    send() 
    close() 
    exit thread()

即每個客戶端在一個獨立的線程中處理。
當一個客戶端的線程執(zhí)行網(wǎng)絡(luò)操作需要等待時,會被操作系統(tǒng)調(diào)度出去,執(zhí)行其他需要干活兒的線程。
似乎完美了解決了我們的問題?
然而并沒有。
因為操作系統(tǒng)創(chuàng)建線程的開銷是比較大的,能夠支持的線程數(shù)量是有限的,通常是幾萬的級別,如果線程太多,就會有很多的CPU浪費在了線程的創(chuàng)建、銷毀、調(diào)度等管理操作上。

所以為了充分發(fā)揮CPU的能力,支持更多的并發(fā)數(shù)量,,在Linux上有另外一種處理并發(fā)的方式:
內(nèi)核提供了監(jiān)聽大量網(wǎng)絡(luò)連接(句柄)可讀、可寫等事件的機制和接口。
應用把需要監(jiān)聽對象以及關(guān)心的事件注冊給內(nèi)核,內(nèi)核在有事件達到時通知應用處理。
基于這種機制處理并發(fā)就是事件驅(qū)動。

事件驅(qū)動機制的基本模型是:

create_listen_socket()
register_event_for_listen_socket()
main_loop:
    wait_for_event()
    check_events:
         if listen_socket has event(new client coming) :
               accept()
               register_event_for_client_socket()
        if client_socket has event(new data coming):
               recv()                       
               parse()
               send()              

但這里有一個問題,有可能一個客戶端剛讀取了一部分數(shù)據(jù),就沒了,剩下的還在網(wǎng)絡(luò)中沒過來,需要繼續(xù)等待。
這就需要把當前的讀取內(nèi)容和請求處理狀態(tài)(也即上下文)保存起來,繼續(xù)處理其他客戶端的事件。
然后下次這個客戶端再有事件到來時再找回上下文繼續(xù)處理。
這其實需要應用自己做一些任務(wù)調(diào)度相關(guān)的上下文保存和切換工作。

當使用多線程處理并發(fā)時,操作系統(tǒng)幫我們做了這些工作,我們無需關(guān)心任務(wù)切換。
因為一個線程就只處理一個客戶端,反復調(diào)用recv把一個請求的數(shù)據(jù)讀完然后解析處理就可以了,也不用擔心沒數(shù)據(jù)到來時,recv阻塞了其他客戶端的處理。
所以多線程編寫并發(fā)代碼非常簡單直接。

如上,事件驅(qū)動機制是Linux上解決并發(fā)問題的一種高效編程模型。
應用反復探測事件,對接收到的事件進行逐個處理的過程就是事件循環(huán)。

那么同步和異步概念體現(xiàn)在哪里呢?

所謂同步就是我們執(zhí)行一個任務(wù),一直等待任務(wù)執(zhí)行結(jié)束。
所謂異步就是我們執(zhí)行一個任務(wù),不等待任務(wù)執(zhí)行結(jié)束,繼續(xù)去干其他活兒,任務(wù)結(jié)果后有個通知,或者干脆不關(guān)心任務(wù)的執(zhí)行結(jié)果。

在多線程模型中,每接收到一個新的客戶端就創(chuàng)建一個線程處理,這就是一種異步處理。
在事件驅(qū)動模型中,當沒有數(shù)據(jù)可讀時,就把這個客戶端繼續(xù)放到監(jiān)聽隊列中監(jiān)聽,也是一種異步。

如果我們考慮文件IO,把IO請求丟給另外一個或一組線程(線程池)處理,處理完后通知主線程,也是一種異步。

2017年8月2日 17:37