您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Go語言中Goroutine和協(xié)程有什么用”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
1.協(xié)程簡(jiǎn)介
大家對(duì)于進(jìn)程、線程都很熟悉,但協(xié)程就沒有火了,協(xié)程并不是Go語言特有的機(jī)制,相反像Lua、Ruby、Python、Kotlin、C/C++等也都有協(xié)程的支持,區(qū)別在于有的是從語言層面支持、有的通過插件類庫支持。Go語言是原生語言層面支持,本文也是從Go角度去理解協(xié)程。
1.1 協(xié)程基本概念和提出者
協(xié)程英文是Coroutine譯為協(xié)同程序“
協(xié)同程序是一種計(jì)算機(jī)程序組件,它允許暫停和恢復(fù)執(zhí)行,從而可以作為通用化的非搶占式多任務(wù)處理子程序。
協(xié)同程序非常適合實(shí)現(xiàn)例如協(xié)作任務(wù)、異常、事件循環(huán)、迭代器、管道等熟悉的程序組件。
根據(jù)唐納德·克努特的說法,梅爾文·康威在1958年將Coroutine這個(gè)術(shù)語應(yīng)用于裝配程序的構(gòu)建,直到在1963年才首次發(fā)表了闡述Coroutine的論文。
1.2 協(xié)程和進(jìn)線程的對(duì)比
我們來復(fù)習(xí)一下進(jìn)線程和協(xié)程的一些基本特點(diǎn)吧:
進(jìn)程是系統(tǒng)資源分配的最小單位, 進(jìn)程包括文本段text region、數(shù)據(jù)段data region和堆棧段stack region等。進(jìn)程的創(chuàng)建和銷毀都是系統(tǒng)資源級(jí)別的,因此是一種比較昂貴的操作,進(jìn)程是搶占式調(diào)度其有三個(gè)狀態(tài):等待態(tài)、就緒態(tài)、運(yùn)行態(tài)。進(jìn)程之間是相互隔離的,它們各自擁有自己的系統(tǒng)資源, 更加安全但是也存在進(jìn)程間通信不便的問題。
進(jìn)程是線程的載體容器,多個(gè)線程除了共享進(jìn)程的資源還擁有自己的一少部分獨(dú)立的資源,因此相比進(jìn)程而言更加輕量,進(jìn)程內(nèi)的多個(gè)線程間的通信比進(jìn)程容易,但是也同樣帶來了同步和互斥的問題和線程安全問題,盡管如此多線程編程仍然是當(dāng)前服務(wù)端編程的主流,線程也是CPU調(diào)度的最小單位,多線程運(yùn)行時(shí)就存在線程切換問題,其狀態(tài)轉(zhuǎn)移如圖:
協(xié)程在有的資料中稱為微線程或者用戶態(tài)輕量級(jí)線程,協(xié)程調(diào)度不需要內(nèi)核參與而是完全由用戶態(tài)程序來決定,因此協(xié)程對(duì)于系統(tǒng)而言是無感知的。協(xié)程由用戶態(tài)控制就不存在搶占式調(diào)度那樣強(qiáng)制的CPU控制權(quán)切換到其他進(jìn)線程,多個(gè)協(xié)程進(jìn)行協(xié)作式調(diào)度,協(xié)程自己主動(dòng)把控制權(quán)轉(zhuǎn)讓出去之后,其他協(xié)程才能被執(zhí)行到,這樣就避免了系統(tǒng)切換開銷提高了CPU的使用效率。
搶占式調(diào)度和協(xié)作式調(diào)度的簡(jiǎn)單對(duì)比:
看到這里我們不免去想:看著協(xié)作式調(diào)度優(yōu)點(diǎn)更多,那么為什么一直是搶占式調(diào)度占上風(fēng)呢?讓我們繼續(xù)一起學(xué)習(xí),可能就能解答這個(gè)問題了。
1.3 實(shí)際工作中的我們
我們寫程序的時(shí)經(jīng)常需要考慮的因素就是提高機(jī)器使用率,這個(gè)非常好理解。當(dāng)然機(jī)器使用率和開發(fā)效率維護(hù)成本往往存在權(quán)衡,說句大白話就是:要么費(fèi)人力要么費(fèi)機(jī)器,選一個(gè)吧!
機(jī)器成本里面最貴的就是CPU了,程序一般分為CPU密集型和IO密集型,對(duì)于CPU密集型我們的優(yōu)化空間可能沒那么多,但對(duì)于IO密集型卻有非常大的優(yōu)化空間,試想我們的程序總是處于IO等待中讓CPU呼呼睡大覺,那該多糟糕。
為了提高IO密集型程序的CPU使用率,我們嘗試多進(jìn)程/多線程編程等讓多個(gè)任務(wù)一起跑分時(shí)復(fù)用搶占式調(diào)度,這樣提高了CPU的利用率,但由于多個(gè)進(jìn)線程存在調(diào)度切換,這也有一定的資源消耗,因此進(jìn)線程數(shù)量不可能無限增大。
我們現(xiàn)在寫的程序大部分都是同步IO的,效率還不夠高,因此出現(xiàn)了一些異步IO框架,但是異步框架的編程難度比同步框架要大,但不可否認(rèn)異步是一個(gè)很好的優(yōu)化方向,先不要暈,來看下同步IO和異步IO就知道了:
同步是指應(yīng)用程序發(fā)起I/O請(qǐng)求后需要等待或者輪詢內(nèi)核I/O操作完成后才能繼續(xù)執(zhí)行,異步是指應(yīng)用程序發(fā)起I/O請(qǐng)求后仍繼續(xù)執(zhí)行,當(dāng)內(nèi)核I/O操作完成后會(huì)通知應(yīng)用程序或者調(diào)用應(yīng)用程序注冊(cè)的回調(diào)函數(shù)。
我們以C/C++開發(fā)的服務(wù)端程序?yàn)槔琇inux的異步IO出現(xiàn)的比較晚,因此像epoll之類的IO復(fù)用技術(shù)仍然有相當(dāng)大的地盤,但是同步IO的效率畢竟不如異步IO,因此當(dāng)前的優(yōu)化方向包括:異步IO框架(像boost.asio框架)和協(xié)程方案(騰訊libco)。
2.Go和協(xié)程
我們知道協(xié)程是Coroutine,Go語言在語言層面對(duì)協(xié)程進(jìn)行了原生支持并且稱之為Goroutine,這也是Go語言強(qiáng)大并發(fā)能力的重要支撐,Go的CSP并發(fā)模型是通過Goroutine和channel來實(shí)現(xiàn)的,后續(xù)會(huì)專門寫一下CSP并發(fā)模型。
2.1 協(xié)作式調(diào)度和調(diào)度器
協(xié)作式調(diào)度中用戶態(tài)協(xié)程會(huì)主動(dòng)讓出CPU控制權(quán)來讓其他協(xié)程使用,確實(shí)提高了CPU的使用率,但是不由得去思考用戶態(tài)協(xié)程不夠智能怎么辦?不知道何時(shí)讓出控制權(quán)也不知道何時(shí)恢復(fù)執(zhí)行。
讀到這里忽然明白了搶占式調(diào)度的優(yōu)勢(shì)了,在搶占式調(diào)度中都是由系統(tǒng)內(nèi)核來完成的,用戶態(tài)不需要參與,并且內(nèi)核參與使得平臺(tái)移植好,說到底還是各有千秋啊!
為了解決這個(gè)問題我們需要一個(gè)中間層來調(diào)度這些協(xié)程,這樣才能讓用戶態(tài)的成千上萬個(gè)協(xié)程穩(wěn)定有序地跑起來,我們姑且把這個(gè)中間層稱為用戶態(tài)協(xié)程調(diào)度器吧!
2.2 Goroutine和Go的調(diào)度器模型
Go語言從2007年底開發(fā)直到今天已經(jīng)發(fā)展了12年,Go的調(diào)度器也不是一蹴而就的,在最初的幾個(gè)版本中Go的調(diào)度器也非常簡(jiǎn)陋,無法支撐大并發(fā)。
經(jīng)過多個(gè)版本的迭代和優(yōu)化,目前已經(jīng)有很優(yōu)異的性能了,不過我們還是來回顧一下Go調(diào)度器的發(fā)展歷程。
Go的調(diào)度器非常復(fù)雜,篇幅所限本文只提一些基本的概念和原理,后續(xù)會(huì)深入去展開Go的調(diào)度器。
最近幾個(gè)版本的Go調(diào)度器采用GPM模型,其中有幾個(gè)概念先看下:
GPM模型使用一種M:N的調(diào)度器來調(diào)度任意數(shù)量的協(xié)程運(yùn)行于任意數(shù)量的系統(tǒng)線程中,從而保證了上下文切換的速度并且利用多核,但是增加了調(diào)度器的復(fù)雜度。
整個(gè)GPM調(diào)度的簡(jiǎn)單過程如下:
新創(chuàng)建的Goroutine會(huì)先存放在Global全局隊(duì)列中,等待Go調(diào)度器進(jìn)行調(diào)度,隨后Goroutine被分配給其中的一個(gè)邏輯處理器P,并放到這個(gè)邏輯處理器對(duì)應(yīng)的Local本地運(yùn)行隊(duì)列中,最終等待被邏輯處理器P執(zhí)行即可。
在M與P綁定后,M會(huì)不斷從P的Local隊(duì)列中無鎖地取出G,并切換到G的堆棧執(zhí)行,當(dāng)P的Local隊(duì)列中沒有G時(shí),再從Global隊(duì)列中獲取一個(gè)G,當(dāng)Global隊(duì)列中也沒有待運(yùn)行的G時(shí),則嘗試從其它的P竊取部分G來執(zhí)行相當(dāng)于P之間的負(fù)載均衡。
“Go語言中Goroutine和協(xié)程有什么用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。