Goroutine(協程)爲何能處理大併發?

簡單來說:協程十分輕量,可以在一個進程中執行有數以十萬計的協程,依舊保持高性能。

進程、線程、協程的關係和區別:

進程擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,進程由操作系統調度。
線程擁有自己獨立的棧和共享的堆,共享堆,不共享棧,線程亦由操作系統調度(標準線程是的)。
協程和線程一樣共享堆,不共享棧,協程由程序員在協程的代碼裏顯示調度。
堆和棧的區別請參看:http://www.cnblogs.com/ghj1976/p/3623037.html

協程和線程的區別是:協程避免了無意義的調度,由此可以提高性能,但也因此,程序員必須自己承擔調度的責任。

執行協程只需要極少的棧內存(大概是4~5KB),默認情況下,線程棧的大小爲1MB。

goroutine就是一段代碼,一個函數入口,以及在堆上爲其分配的一個堆棧。所以它非常廉價,我們可以很輕鬆的創建上萬個goroutine,但它們並不是被操作系統所調度執行。

和所有其他併發框架裏的協程一樣,goroutine裏所謂“無鎖”的優點只在單線程下有效,如果$GOMAXPROCS > 1並且協程間需要通信,Go運行庫會負責加鎖保護數據,這也是爲什麼sieve.go這樣的例子在多CPU多線程時反而更慢的原因.

http://my.oschina.net/Obahua/blog/144549

goroutine 的一個主要特性就是它們的消耗;創建它們的初始內存成本很低廉(與需要 1 至 8MB 內存的傳統 POSIX 線程形成鮮明對比)以及根據需要動態增長和縮減佔用的資源。這使得 goroutine 會從 4096 字節的初始棧內存佔用開始按需增長或縮減內存佔用,而無需擔心資源的耗盡。

爲了實現這個目標,鏈接器(5l、6l 和 8l)會在每個函數前插入一個序文,這個序文會在函數被調用之前檢查判斷當前的資源是否滿足調用該函數的需求(備註 1)。如果不滿足,則調用 runtime.morestack 來分配新的棧頁面(備註 2),從函數的調用者那裏拷貝函數的參數,然後將控制權返回給調用者。此時,已經可以安全地調用該函數了。當函數執行完畢,事情並沒有就此結束,函數的返回參數又被拷貝至調用者的棧結構中,然後釋放無用的棧空間。

通過這個過程,有效地實現了棧內存的無限使用。假設你並不是不斷地在兩個棧之間往返,通俗地講叫棧分割,則代價是十分低廉的。

參考資料:

【翻譯】爲什麼 goroutine 的棧內存無窮大?
http://my.oschina.net/Obahua/blog/144549

進程、線程和協程的理解
http://blog.leiqin.name/2012/12/02/%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%8D%8F%E7%A8%8B%E7%9A%84%E7%90%86%E8%A7%A3.html

協程框架的堆棧大小陷阱
http://blog.csdn.net/huyiyang2010/article/details/6104891

Coroutine及其實現
http://www.cnblogs.com/foxmailed/archive/2014/01/08.html

goroutine背後的系統知識
http://www.sizeofvoid.net/goroutine-under-the-hood/

【轉載】https://www.cnblogs.com/ghj1976/p/3642513.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章