Go語言: 萬物皆異步

2018.5.29更新:
修正了對go協(xié)程調(diào)度器描述上的錯誤。

2018.11.7更新:
添加了對網(wǎng)絡(luò)I/O的說明

同步和異步、阻塞和非阻塞

首先要明確的是,同步(Synchronous)和異步(Asynchronous),阻塞(Blocking)和非阻塞(Non-Blocking)是兩種完全不同的概念。前者指的是一種事件通知、處理機制,而后者則是程序控制流程的差異。

我們以A調(diào)用B為例來說明兩者之間的區(qū)別:

  • 阻塞
    只有當B任務(wù)完成后,程序控制權(quán)才會返回給A, A得以繼續(xù)執(zhí)行。

  • 非阻塞
    B馬上返回,此時B并沒有完成,但A可以繼續(xù)執(zhí)行,B任務(wù)并不影響
    A的執(zhí)行。

  • 同步
    A需要通過某種方式主動查詢B是否完成。在blocking模式中,它表現(xiàn)為等待B返回;在non-blocking模式中表現(xiàn)為通過Future對象詢問B是否完成,如果完成則取出結(jié)果,未完成則等待(阻塞)。

  • 異步
    A在啟動B任務(wù)時就不管了,繼續(xù)執(zhí)行自己的任務(wù),當B完成時,由操作系統(tǒng)主動通知A,告知B已經(jīng)完成。A可以在適宜的時候取出B的執(zhí)行結(jié)果。在這種模式下,A完全不會因為B的執(zhí)行而影響自己。

異步是解決web應(yīng)用高并發(fā)的唯一方案

“傳統(tǒng)的”一線程對一請求的模型直接決定了單機并不能處理過多的并發(fā)請求,而且這種模型下會導(dǎo)致很大的線程資源浪費。這里面原因有二:一是每條線程占用要使用較多的內(nèi)存,在JVM中每創(chuàng)建一個線程就要消耗2M多的heap,于是內(nèi)存大小就變成線程數(shù)量的瓶頸;二是當線程數(shù)據(jù)超過CPU核心數(shù)時,頻繁的線程切換會變成一筆可觀的開銷,而且當你的程序因為查詢數(shù)據(jù)庫、執(zhí)行RPC調(diào)用阻塞當前線程時,這個線程是完全不能運行的,不僅白白占用了內(nèi)存,還增加了Context Switch的次數(shù)。

異步并不能加快你對于單個請求的處理速度,但是它能最大化的消滅資源浪費,從而大大提高單機并發(fā)極限。

Go的世界中,萬物皆異步

Go中只有協(xié)程,而協(xié)程本質(zhì)上就是異步。

為什么這么說呢?首先我們知道,協(xié)程(routine)跟線程是多對一的關(guān)系,routine本身不會被調(diào)度執(zhí)行,它只能依靠操作系統(tǒng)的線程來運行。一個線程可以執(zhí)行多個routine, Go運行時調(diào)度器負責進行調(diào)度處理。routine只有三種情況需要讓出執(zhí)行權(quán),分別是system call, 鎖競爭和主動讓出執(zhí)行權(quán)力。

  • system call
    當發(fā)生系統(tǒng)調(diào)用時,執(zhí)行當前routine的線程會block這是沒得商量的,但go運行時會從它維護的線程池中取出一條空閑線程繼續(xù)執(zhí)行其它routine, 這樣就做到了即使你block了routine,也不會影響其它routine的執(zhí)行。這里可以類比于Netty中的I/O線程,如果你代碼block了,則會卡住其他任務(wù)的執(zhí)行,否則你必須在自己的線程池中執(zhí)行會產(chǎn)生block的代碼。

  • 網(wǎng)絡(luò)I/O
    調(diào)用net包下的網(wǎng)絡(luò)I/O操作是不會阻塞線程的。當發(fā)起網(wǎng)絡(luò)I/O請求時,go運行時會通過操作系統(tǒng)提供的epoll機制注冊I/O事件,不會掛起實際干活的線程,只會切換goroutine而已。

  • 競爭
    無論是鎖競爭還是讀寫channel而導(dǎo)致routine被掛起,其背后的線程都是不會有任何block的,在OS看來線程一直在正常運行,從而大大降低了線程上下文切換的開銷。

  • 主動讓出執(zhí)行權(quán)
    同上面的競爭,主動讓出執(zhí)行權(quán)時背后的線程同樣不會block。

仔細想想看,這跟前面講的異步不是幾乎一樣的邏輯嗎?區(qū)別是,Go語言中是Go自己的調(diào)度器來通知routine等待的IO事件是否完成,而其它非協(xié)程語言則是OS來通知。

因此,Go中我們雖然在以同步的方式編寫代碼,但卻與異步有著異曲同工之妙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容