若是你對如下幾個問題有疑問,那麼本文可能會有所幫助。html
1.2.3
linux
談協程繞不開線程,按傳統還得從進程談起,不過我想業內人員對進程和線程應該是耳熟能詳,這裏就簡單歸納下。git
進程擁有本身獨立的堆和棧,既不共享堆,亦不共享棧,進程由操做系統調度;線程擁有本身獨立的棧,共享堆(也能夠有本身的私有域),不共享棧,線程亦由操做系統調度。一個進程能夠有多個線程。github
多線程一直以來是面試必考點,雖然[web]服務端開發人員彷佛曆來不用直接操做線程,實際上是由於框架幫忙維護了,開發人員只須要關心業務實現。這也致使了部分人對多線程的某些概念模糊不清。好比關於多線程的效率:在多核cpu下,多個線程能夠並行運行在不一樣內核上,效率高;而在單核cpu中,多個線程的並行執行實際上是一個錯覺,由於它們都是運行在一個內核上,一個cpu內核同一時間只能執行一個進程/線程,所以在一個內核上的多線程執行其實效率反而比串行執行低,只是給用戶一種併發的錯覺,反而增長了線程切換的時間。web
可是效率的高低還要看線程佔用cpu資源的佔用率,好比存在大量IO操做,IO比較慢。也就是說,若是隻有單線程,那麼一旦涉及到IO操做,線程可能會被阻塞,程序的其他邏輯就只能傻等,就算那些邏輯不依賴於這個IO操做,此時線程對CPU的使用爲0,CPU就是空閒狀態。若是是多線程,是線程瓶頸,那麼其他線程則可使用cpu,而非等待IO結束。面試
題外話,一個空循環就能讓cpu滿載,參看 爲何一個空的死循環會讓CPU佔用達到100%。算法
後來,出現了多路複用之類的技術,原先須要等待IO返回的線程也不須要等了,能夠和其它線程同樣忙別的事,IO返回時獲得通知再處理接下去的事情。Java的NIO和.Net的async/await就是這麼幹的。編程
通常來講,爲了不線程頻繁建立銷燬帶來的性能問題,程序裏都會使用到線程池。windows
然而仍是在單核的場景下,事情彷佛變得有點詭異。既然線程們如今都能心無旁騖地使用CPU計算,而前面也說了,一個cpu內核同時只能運行一個線程,管理多線程又是搶佔式,又是棧切換,維護生命週期啥的,影響性能不說,徹底沒得必要嘛,爲何不僅用一個線程完成全部的計算呢。什麼,你說可能須要[僞]並行計算?那就讓線程本身來安排咯,畢竟具體邏輯方面,線程自己(或者說開發人員)比CPU要清楚的多,知道何時該幹什麼,何時切換邏輯,何時不切換,都由線程本身說了算。因而,協程粉墨登場。api
協程主要是針對單線程的一個概念(如Js、NodeJs、Python因爲GIL致使的僞多線程),能夠將其看做線程運行時片斷。和線程相似,雖然貌似多個協程能夠並行執行,一個時間仍然只能運行一個。因此,若是業務邏輯是順序相關(串行)或者各任務對反饋及時性要求不高,那麼不必用協程,就跟不必多線程同樣。協程對比線程,除了有更好的性能外,還讓開發人員對執行片斷有了更好的掌控。好比Go語言,經過阻塞條件(time.sleep()、select{}等),咱們能夠手動將控制權轉移給其它的 Go 協程 , 也能夠說是告訴調度器讓它去調度其它可用空閒的 Go 協程(Go如何判斷這是阻塞代碼還沒有研究過);或者經過channel調度指定協程。
Go默認狀況下只用單線程。這就是說,你即便開了幾百個goroutine,系統中同一時間在跑的只有一個線程,也就是一個協程。依據上面的內容,你們能夠思考下Go爲什麼默認如此。咱們能夠經過 runtime.GOMAXPROCS() 設置的是Go語言能跑幾個線程,講道理,CPU幾核跑幾個線程比較合理,使用 runtime.NumCPU() 查看內核數。
在編程層面來講,協程的概念偏向於以同步編程的模式實現異步處理的編程模式,避免了多層回調代碼嵌套的問題。
其實在不少年之前,協程已經被提出了,如今只是它煥發生機的階段。
4
上文說了,協程之間應該是非順序相關的,即它們的上下文沒有強依賴關係,是相對獨立的。這裏的上下文指的就是當前的運行棧空間,它包括了參數、局部變量、各寄存器的值等內容。在協程切換的時候,咱們要想辦法將對應的上下文投射到當前線程的運行棧中,即讓線程執行特定的上下文。很容易想到malloc一塊臨時內存存放掛起的協程上下文信息,resume的時候再覆蓋回去,運行棧在內存中只有一處,這就是stackless模式。相對的還有stackful模式,在這種模式下,每一個協程都有本身的棧空間,運行棧指的就是當前協程的棧空間。現有語言的實現中,Python, Kotlin等定義的就是stackless協程, Go語言中實現的是stackful協程。
對於其它沒有在語言層面直接支持協程的語言來講,因爲協程涉及到底層的[堆]棧切換控制,所以很難單純依靠現有語法構建算法的方式實現。有人作過此類嘗試(可參看Coroutines in C),但也沒有實用性。
能直接操做執行堆棧並暴露api的,如今市面上的語言以C/C++最爲流行,基於它們也有不少開源的協程庫。下面介紹幾種實現方式。
協程分爲非對稱協程和對稱協程。在非對稱協程中,調用者和被調用者的關係是固定的,調用者將控制流轉到被調用者,被調用者運行完畢後只能返回到調用者,而不能返回到其餘協程。對稱協程則否則。對稱協程能夠很容易由非對稱協程來表達。且按通常的調用邏輯,A調B,B應返回到A,再由A發起到C的調用,而非B直接返回到C。所以,目前大多數協程庫都只實現非對稱協程。
關於彙編語法的平臺差別,類Unix下采用的是AT&T的彙編語法格式,Dos/Windows下面採用的是Intel彙編語法格式。
參考資料:
轉載請註明本文出處:http://www.javashuo.com/article/p-hcsueoiu-gh.html