国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Go 語言 Goroutines和線程

2023-03-14 16:58 更新

原文鏈接:https://gopl-zh.github.io/ch9/ch9-08.html


9.8. Goroutines和線程

在上一章中我們說goroutine和操作系統(tǒng)的線程區(qū)別可以先忽略。盡管兩者的區(qū)別實際上只是一個量的區(qū)別,但量變會引起質(zhì)變的道理同樣適用于goroutine和線程。現(xiàn)在正是我們來區(qū)分開兩者的最佳時機。

9.8.1. 動態(tài)棧

每一個OS線程都有一個固定大小的內(nèi)存塊(一般會是2MB)來做棧,這個棧會用來存儲當(dāng)前正在被調(diào)用或掛起(指在調(diào)用其它函數(shù)時)的函數(shù)的內(nèi)部變量。這個固定大小的棧同時很大又很小。因為2MB的棧對于一個小小的goroutine來說是很大的內(nèi)存浪費,比如對于我們用到的,一個只是用來WaitGroup之后關(guān)閉channel的goroutine來說。而對于go程序來說,同時創(chuàng)建成百上千個goroutine是非常普遍的,如果每一個goroutine都需要這么大的棧的話,那這么多的goroutine就不太可能了。除去大小的問題之外,固定大小的棧對于更復(fù)雜或者更深層次的遞歸函數(shù)調(diào)用來說顯然是不夠的。修改固定的大小可以提升空間的利用率,允許創(chuàng)建更多的線程,并且可以允許更深的遞歸調(diào)用,不過這兩者是沒法同時兼?zhèn)涞摹?/p>

相反,一個goroutine會以一個很小的棧開始其生命周期,一般只需要2KB。一個goroutine的棧,和操作系統(tǒng)線程一樣,會保存其活躍或掛起的函數(shù)調(diào)用的本地變量,但是和OS線程不太一樣的是,一個goroutine的棧大小并不是固定的;棧的大小會根據(jù)需要動態(tài)地伸縮。而goroutine的棧的最大值有1GB,比傳統(tǒng)的固定大小的線程棧要大得多,盡管一般情況下,大多goroutine都不需要這么大的棧。

練習(xí) 9.4: 創(chuàng)建一個流水線程序,支持用channel連接任意數(shù)量的goroutine,在跑爆內(nèi)存之前,可以創(chuàng)建多少流水線階段?一個變量通過整個流水線需要用多久?(這個練習(xí)題翻譯不是很確定)

9.8.2. Goroutine調(diào)度

OS線程會被操作系統(tǒng)內(nèi)核調(diào)度。每幾毫秒,一個硬件計時器會中斷處理器,這會調(diào)用一個叫作scheduler的內(nèi)核函數(shù)。這個函數(shù)會掛起當(dāng)前執(zhí)行的線程并將它的寄存器內(nèi)容保存到內(nèi)存中,檢查線程列表并決定下一次哪個線程可以被運行,并從內(nèi)存中恢復(fù)該線程的寄存器信息,然后恢復(fù)執(zhí)行該線程的現(xiàn)場并開始執(zhí)行線程。因為操作系統(tǒng)線程是被內(nèi)核所調(diào)度,所以從一個線程向另一個“移動”需要完整的上下文切換,也就是說,保存一個用戶線程的狀態(tài)到內(nèi)存,恢復(fù)另一個線程的到寄存器,然后更新調(diào)度器的數(shù)據(jù)結(jié)構(gòu)。這幾步操作很慢,因為其局部性很差需要幾次內(nèi)存訪問,并且會增加運行的cpu周期。

Go的運行時包含了其自己的調(diào)度器,這個調(diào)度器使用了一些技術(shù)手段,比如m:n調(diào)度,因為其會在n個操作系統(tǒng)線程上多工(調(diào)度)m個goroutine。Go調(diào)度器的工作和內(nèi)核的調(diào)度是相似的,但是這個調(diào)度器只關(guān)注單獨的Go程序中的goroutine(譯注:按程序獨立)。

和操作系統(tǒng)的線程調(diào)度不同的是,Go調(diào)度器并不是用一個硬件定時器,而是被Go語言“建筑”本身進行調(diào)度的。例如當(dāng)一個goroutine調(diào)用了time.Sleep,或者被channel調(diào)用或者mutex操作阻塞時,調(diào)度器會使其進入休眠并開始執(zhí)行另一個goroutine,直到時機到了再去喚醒第一個goroutine。因為這種調(diào)度方式不需要進入內(nèi)核的上下文,所以重新調(diào)度一個goroutine比調(diào)度一個線程代價要低得多。

練習(xí) 9.5:  寫一個有兩個goroutine的程序,兩個goroutine會向兩個無buffer channel反復(fù)地發(fā)送ping-pong消息。這樣的程序每秒可以支持多少次通信?

9.8.3. GOMAXPROCS

Go的調(diào)度器使用了一個叫做GOMAXPROCS的變量來決定會有多少個操作系統(tǒng)的線程同時執(zhí)行Go的代碼。其默認的值是運行機器上的CPU的核心數(shù),所以在一個有8個核心的機器上時,調(diào)度器一次會在8個OS線程上去調(diào)度GO代碼。(GOMAXPROCS是前面說的m:n調(diào)度中的n)。在休眠中的或者在通信中被阻塞的goroutine是不需要一個對應(yīng)的線程來做調(diào)度的。在I/O中或系統(tǒng)調(diào)用中或調(diào)用非Go語言函數(shù)時,是需要一個對應(yīng)的操作系統(tǒng)線程的,但是GOMAXPROCS并不需要將這幾種情況計算在內(nèi)。

你可以用GOMAXPROCS的環(huán)境變量來顯式地控制這個參數(shù),或者也可以在運行時用runtime.GOMAXPROCS函數(shù)來修改它。我們在下面的小程序中會看到GOMAXPROCS的效果,這個程序會無限打印0和1。

for {
    go fmt.Print(0)
    fmt.Print(1)
}

$ GOMAXPROCS=1 go run hacker-cliche?.go
111111111111111111110000000000000000000011111...

$ GOMAXPROCS=2 go run hacker-cliche?.go
010101010101010101011001100101011010010100110...

在第一次執(zhí)行時,最多同時只能有一個goroutine被執(zhí)行。初始情況下只有main goroutine被執(zhí)行,所以會打印很多1。過了一段時間后,GO調(diào)度器會將其置為休眠,并喚醒另一個goroutine,這時候就開始打印很多0了,在打印的時候,goroutine是被調(diào)度到操作系統(tǒng)線程上的。在第二次執(zhí)行時,我們使用了兩個操作系統(tǒng)線程,所以兩個goroutine可以一起被執(zhí)行,以同樣的頻率交替打印0和1。我們必須強調(diào)的是goroutine的調(diào)度是受很多因子影響的,而runtime也是在不斷地發(fā)展演進的,所以這里的你實際得到的結(jié)果可能會因為版本的不同而與我們運行的結(jié)果有所不同。

練習(xí)9.6: 測試一下計算密集型的并發(fā)程序(練習(xí)8.5那樣的)會被GOMAXPROCS怎樣影響到。在你的電腦上最佳的值是多少?你的電腦CPU有多少個核心?

9.8.4. Goroutine沒有ID號

在大多數(shù)支持多線程的操作系統(tǒng)和程序語言中,當(dāng)前的線程都有一個獨特的身份(id),并且這個身份信息可以以一個普通值的形式被很容易地獲取到,典型的可以是一個integer或者指針值。這種情況下我們做一個抽象化的thread-local storage(線程本地存儲,多線程編程中不希望其它線程訪問的內(nèi)容)就很容易,只需要以線程的id作為key的一個map就可以解決問題,每一個線程以其id就能從中獲取到值,且和其它線程互不沖突。

goroutine沒有可以被程序員獲取到的身份(id)的概念。這一點是設(shè)計上故意而為之,由于thread-local storage總是會被濫用。比如說,一個web server是用一種支持tls的語言實現(xiàn)的,而非常普遍的是很多函數(shù)會去尋找HTTP請求的信息,這代表它們就是去其存儲層(這個存儲層有可能是tls)查找的。這就像是那些過分依賴全局變量的程序一樣,會導(dǎo)致一種非健康的“距離外行為”,在這種行為下,一個函數(shù)的行為可能并不僅由自己的參數(shù)所決定,而是由其所運行在的線程所決定。因此,如果線程本身的身份會改變——比如一些worker線程之類的——那么函數(shù)的行為就會變得神秘莫測。

Go鼓勵更為簡單的模式,這種模式下參數(shù)(譯注:外部顯式參數(shù)和內(nèi)部顯式參數(shù)。tls 中的內(nèi)容算是"外部"隱式參數(shù))對函數(shù)的影響都是顯式的。這樣不僅使程序變得更易讀,而且會讓我們自由地向一些給定的函數(shù)分配子任務(wù)時不用擔(dān)心其身份信息影響行為。

你現(xiàn)在應(yīng)該已經(jīng)明白了寫一個Go程序所需要的所有語言特性信息。在后面兩章節(jié)中,我們會回顧一些之前的實例和工具,支持我們寫出更大規(guī)模的程序:如何將一個工程組織成一系列的包,如何獲取,構(gòu)建,測試,性能測試,剖析,寫文檔,并且將這些包分享出去。



以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號