Goroutine是如何工作的?

翻譯原文鏈接? ?轉(zhuǎn)帖/轉(zhuǎn)載請注明出處

英文原文鏈接 發(fā)表于2014/02/24

Go語言

如果你剛剛接觸Go語言从橘,或者說你并不理解“并發(fā)不等于并行”這句話的含義纪岁,那么Rob Pike的講座值得一看(在youtube上)代嗤。這個視頻有30分鐘長涵妥,我保證花30分鐘看這段視頻是非常值得的促脉。

這里摘錄一段他提到的并發(fā)和并行之間的區(qū)別:“當大家聽到并發(fā)這個詞的時候悦污,他們往往想到的是并行。并行是一個相關悼沈,但卻完全不同的概念贱迟。當我們編程的時候姐扮,并發(fā)指的是多個獨立運行的進程絮供,而并行是指同時運行的多個計算衣吠。并發(fā)是為了一下子處理很多東西。并行是為了同時做很多事情壤靶「壳危” [1] (注:這里的概念有點繞。其實本質(zhì)的區(qū)別在“同時”這個詞上贮乳。并行強調(diào)的時候幾個進程同時進行忧换。而并發(fā)指的是運行多個進程,但這些進程并不需要同時被執(zhí)行向拆。它們可以是被調(diào)度在同一個CPU分時運行的亚茬。)

Go為我們寫并發(fā)程序提供了便利。它提供了goroutine以及它們之間通信的功能浓恳。在這里我們主要討論goroutine刹缝。

Goroutine和線程的區(qū)別

Go語言使用的是goroutine,而像Java這樣的語言大多使用線程颈将。它們之間的區(qū)別是什么呢梢夯?讓我們從三個方面來看看它們的區(qū)別:內(nèi)存占用,創(chuàng)建和銷毀晴圾,以及切換開銷颂砸。

內(nèi)存占用

創(chuàng)建一個goroutine不需要太多的內(nèi)存 - 大概2KB左右的棧空間死姚。如果需要更多的椚伺遥空間,就從堆里分配額外的空間來使用都毒。[2][3] 新創(chuàng)建的線程會占用1MB的內(nèi)存空間(這大約是goroutine的500倍)色罚。這還不包括守護頁(guard page)的空間。守護頁是用來保護線程之間的內(nèi)存空間不會被相互竄改温鸽。[7]

因此一個處理很多請求的服務可以為每個請求創(chuàng)建一個goroutine保屯。但是如果為每個請求去創(chuàng)建一個線程,那么它很快就會碰到OutOfMemoryError涤垫。這不是Java獨有的問題姑尺,任何使用操作系統(tǒng)線程作為主要并發(fā)手段的編程語言都會碰到這個問題。

創(chuàng)建和銷毀的開銷

線程需要從操作系統(tǒng)里請求資源并在用完之后釋放回去蝠猬,因此創(chuàng)建和銷毀線程的開銷非常大切蟋。為了避免這些開銷,我們通常的做法是維護一個線程池榆芦。Goroutine的創(chuàng)建和銷毀是由運行環(huán)境(runtime)完成的柄粹。這些操作的開銷就比較小喘鸟。Go語言不支持手工管理goroutine。

切換開銷

當一個線程阻塞的時候驻右,另外一個線程需要被調(diào)度到當前處理器上運行什黑。線程的調(diào)度是搶占式的(preemptively)。當切換一個線程的時候堪夭,調(diào)度器需要保存/恢復所有的寄存器愕把。這包括16個通用寄存器,程序指針(program counter)森爽,棧指針(stack pointer)恨豁,段寄存器(segment registers)和16個XMM寄存器,浮點協(xié)處理器狀態(tài)爬迟,16個AVX寄存器橘蜜,所有的特殊模塊寄存器(MSR)等。當在線程間快速切換的時候這些開銷就變得非常大了付呕。

Goroutine的調(diào)度是協(xié)同合作式的(cooperatively)计福。當切換goroutine的時候,調(diào)度器只需要保存和恢復三個寄存器 - 程序指針凡涩,棧指針和DX棒搜。切換的開銷就小多了。

前面已經(jīng)談到了活箕,goroutine的數(shù)目會比線程多很多力麸,但這并不影響切換的時間。有兩個原因:第一育韩,只有可以運行的goroutine才會被考慮克蚂,正在阻塞的goroutine會被忽略。第二筋讨,現(xiàn)代的調(diào)度器的復雜度都是O(1)的埃叭。這意味著選擇的數(shù)目(線程或者是goroutine)不會影響切換的時間。[5]

Goroutine的運行

前面談到悉罕,運行環(huán)境負責goroutine的創(chuàng)建赤屋,調(diào)度和銷毀。運行環(huán)境被會分配一些線程壁袄,用來運行所有的goroutine类早。在任何一個時間點,每個線程只會運行一個goroutine嗜逻。如果一個goroutine被阻塞涩僻,另外一個goroutine會來替換它在對應的線程上運行。[6]

因為goroutine的調(diào)度是協(xié)同合作式的,如果一個goroutine不停的循環(huán)逆日,其它的goroutine就沒有機會被調(diào)度運行了嵌巷。在Go 1.2里,這個問題的解決辦法是在調(diào)用一個函數(shù)的時候去偶爾觸發(fā)Go的調(diào)度器室抽。這樣一個循環(huán)里如果調(diào)用了沒有被內(nèi)聯(lián)的函數(shù)搪哪,它就可以被搶占了。

Goroutine的阻塞

Goroutine是廉價的狠半,在下面這些阻塞情況下它們也不會造成運行的線程被阻塞:

- 網(wǎng)絡收發(fā)

- 睡眠

- channel操作

- sync包里的一些會阻塞的基本操作

即使創(chuàng)建了成千上萬的goroutine并且大多數(shù)被阻塞了噩死,也不會造成太多的系統(tǒng)資源浪費颤难。因為運行環(huán)境會調(diào)度另外的goroutine來運行神年。

簡而言之,goroutine是對線程的輕量化抽象行嗤。Go語言的程序員不需要直接操作線程已日。與此同時操作系統(tǒng)也不知道goroutine的存在。從操作系統(tǒng)的角度來看栅屏,一個Go程序有點像一個事件驅(qū)動的C程序飘千。[5]

線程和處理器

雖然我們不能直接控制運行環(huán)境創(chuàng)建多少線程,我們可以設置程序使用的處理器核數(shù)栈雳。這是通過調(diào)用runtime.GOMAXPROCS(n)函數(shù)設置GOMAXPROCS變量來實現(xiàn)的护奈。(注:也可以通過直接設置環(huán)境變量來控制)。增加處理器核數(shù)并不意味著程序性能的提高哥纫。這取決于程序本身的設計霉旗。你的程序需要用到多少個內(nèi)核數(shù)可以用剖析(profiling)工具來找到答案。

結束語

和其它語言類似蛀骇,避免多個goroutine同時訪問一個共享資源是非常重要的厌秒。goroutine之間,最好是用channel來傳輸數(shù)據(jù)擅憔。有興趣的可以讀一讀“do not communicate by sharing memory; instead, share memory by communicating”鸵闪。

最后,我強烈推薦讀一下C. A. R. Hoare寫的“Communicating Sequential Processes”暑诸。他是個天才蚌讼。在這篇論文(1978年發(fā)表的)里,他預測了單核處理器性能最終會遇到瓶頸个榕,然后芯片制造商們會增加處理器的內(nèi)核數(shù)篡石。他的思想對Go語言的設計影響深遠。

參考文獻

1. Concurrency is not parallelism by Rob Pike

2. Effective Go: Goroutines

3. Goroutine stack size was decreased from 8kB to 2kB in Go 1.4

4. Goroutine stacks became contiguous in Go 1.3

5. Scheduling of goroutines by Dmitry Vyukov

6. Analysis of the Go runtime scheduler

7. 5 things that make Go fast by Dave Cheney

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笛洛,一起剝皮案震驚了整個濱河市夏志,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖沟蔑,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湿诊,死亡現(xiàn)場離奇詭異,居然都是意外死亡瘦材,警方通過查閱死者的電腦和手機厅须,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來食棕,“玉大人朗和,你說我怎么就攤上這事〔鞠” “怎么了眶拉?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長憔儿。 經(jīng)常有香客問我忆植,道長,這世上最難降的妖魔是什么谒臼? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任朝刊,我火速辦了婚禮,結果婚禮上蜈缤,老公的妹妹穿的比我還像新娘拾氓。我一直安慰自己,他們只是感情好底哥,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布咙鞍。 她就那樣靜靜地躺著,像睡著了一般叠艳。 火紅的嫁衣襯著肌膚如雪奶陈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天附较,我揣著相機與錄音吃粒,去河邊找鬼。 笑死拒课,一個胖子當著我的面吹牛徐勃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播早像,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼僻肖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了卢鹦?” 一聲冷哼從身側響起臀脏,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后揉稚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秒啦,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年搀玖,在試婚紗的時候發(fā)現(xiàn)自己被綠了余境。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡灌诅,死狀恐怖芳来,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猜拾,我是刑警寧澤即舌,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站关带,受9級特大地震影響侥涵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宋雏,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望务豺。 院中可真熱鬧磨总,春花似錦、人聲如沸笼沥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奔浅。三九已至馆纳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汹桦,已是汗流浹背鲁驶。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舞骆,地道東北人钥弯。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像督禽,于是被迫代替她去往敵國和親脆霎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Goroutine是Go里的一種輕量級線程——協(xié)程狈惫。相對線程睛蛛,協(xié)程的優(yōu)勢就在于它非常輕量級,進行上下文切換的代價非...
    witchiman閱讀 4,834評論 0 9
  • Java8張圖 11、字符串不變性 12忆肾、equals()方法菠红、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,702評論 0 11
  • 從三月份找實習到現(xiàn)在难菌,面了一些公司试溯,掛了不少,但最終還是拿到小米郊酒、百度遇绞、阿里、京東燎窘、新浪摹闽、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,246評論 11 349
  • ———你對我還是沒變 沒變的只是冷漠
    薄情人zmj閱讀 210評論 0 0
  • 小時候褐健,雪是那么的司空見慣 第一場雪也并未顯得多么獨一無二 三五好友乘著下雪 廝混在冰天雪地之中 感受著大自然脈搏...
    鐮鼬悟閱讀 249評論 0 0