您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān) Go協(xié)程實(shí)現(xiàn)原理和使用分析是怎樣的,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
Go 并發(fā)編程原理
Go 語言的協(xié)程實(shí)現(xiàn)被稱之為 goroutine,由 Go 運(yùn)行時(shí)管理,在 Go 語言中通過協(xié)程實(shí)現(xiàn)并發(fā)編程非常簡(jiǎn)單:我們可以在一個(gè)處理進(jìn)程中通過關(guān)鍵字 go
啟用多個(gè)協(xié)程,然后在不同的協(xié)程中完成不同的子任務(wù),這些用戶在代碼中創(chuàng)建和維護(hù)的協(xié)程本質(zhì)上是用戶級(jí)線程,Go 語言運(yùn)行時(shí)會(huì)在底層通過調(diào)度器將用戶級(jí)線程交給操作系統(tǒng)的系統(tǒng)級(jí)線程去處理,如果在運(yùn)行過程中遇到某個(gè) IO 操作而暫停運(yùn)行,調(diào)度器會(huì)將用戶級(jí)線程和系統(tǒng)級(jí)線程分離,以便讓系統(tǒng)級(jí)線程去處理其他用戶級(jí)線程,而當(dāng) IO 操作完成,需要恢復(fù)運(yùn)行,調(diào)度器又會(huì)調(diào)度空閑的系統(tǒng)級(jí)線程來處理這個(gè)用戶級(jí)線程,從而達(dá)到并發(fā)處理多個(gè)協(xié)程的目的。此外,調(diào)度器還會(huì)在系統(tǒng)級(jí)線程不夠用時(shí)向操作系統(tǒng)申請(qǐng)創(chuàng)建新的系統(tǒng)級(jí)線程,而在系統(tǒng)級(jí)線程過多的情況下銷毀一些空閑的線程,這個(gè)過程和 PHP-FPM 的工作機(jī)制有點(diǎn)類似,實(shí)際上這也是很多進(jìn)程/線程池管理器的工作機(jī)制,這樣一來,可以保證對(duì)系統(tǒng)資源的高效利用,避免系統(tǒng)資源的浪費(fèi)。
以上,就是 Go 語言并發(fā)編程的獨(dú)特實(shí)現(xiàn)模型。
下面通過一個(gè)簡(jiǎn)單的示例來演示如何在 Go 語言中通過協(xié)程進(jìn)行并發(fā)編程,我們?cè)?nbsp;add.go
中編寫一個(gè)加法函數(shù) add
并通過協(xié)程的方式來調(diào)用它:
package main
import "fmt"
func add(a, b int) {
var c = a + b;
fmt.Printf("%d + %d = %d", a, b, c)
}
func main() {
go add(1, 2)
}
嗯,就是這么簡(jiǎn)單,在這段代碼中包含了兩個(gè)協(xié)程,一個(gè)是顯式的,通過 go
關(guān)鍵字聲明的這條語句,表示啟用一個(gè)新的協(xié)程來處理加法運(yùn)算,另一個(gè)是隱式的,即 main
函數(shù)本身也是運(yùn)行在一個(gè)主協(xié)程中,該協(xié)程和調(diào)用 add
函數(shù)的子協(xié)程是并發(fā)運(yùn)行的兩個(gè)協(xié)程,就好比從 go
關(guān)鍵字開始,從主協(xié)程中叉出一條新路。和之前不使用協(xié)程的方式相比,由此也引入了不確定性:我們不知道子協(xié)程什么時(shí)候執(zhí)行完畢,運(yùn)行到了什么狀態(tài)。在主協(xié)程中啟動(dòng)子協(xié)程后,程序就退出運(yùn)行了,這就意味著包含這兩個(gè)協(xié)程的處理進(jìn)程退出了,所以,我們運(yùn)行這段代碼,不會(huì)看到子協(xié)程里運(yùn)行的打印結(jié)果,因?yàn)檫€沒來得及執(zhí)行它們,進(jìn)程就已經(jīng)退出了。另外,我們也不要試圖從 add
函數(shù)返回處理結(jié)果,因?yàn)樵谥鲄f(xié)程中,根本獲取不到子協(xié)程的返回值,從子協(xié)程開始執(zhí)行起就已經(jīng)和主協(xié)程沒有任何關(guān)系了,返回值會(huì)被丟棄。
如果要顯示出子協(xié)程的打印結(jié)果,一種方式是在主協(xié)程中等待足夠長的時(shí)間再退出,以便保證子協(xié)程中的所有代碼執(zhí)行完畢:
package main
import (
"fmt"
"time"
)
func add(a, b int) {
var c = a + b;
fmt.Printf("%d + %d = %d\n", a, b, c)
}
func main() {
go add(1, 2)
time.Sleep(1e9)
}
這里,我們通過 time.Sleep(1e9)
讓主協(xié)程等待 1s 后退出,這樣,運(yùn)行 go run add.go
就可以看到打印結(jié)果了:
不過,這種方式過于簡(jiǎn)單粗暴,對(duì)于加法運(yùn)算,1s 肯定夠了(而且根本不需要這么長時(shí)間),但是如果是數(shù)據(jù)庫連接、發(fā)送郵件之類的難以預(yù)估時(shí)間的操作呢?這種方式就不合適了,我們需要一種更精準(zhǔn)的方式在子協(xié)程執(zhí)行完畢后,立即退出主協(xié)程,這就涉及到協(xié)程間的通信,我們將在下一篇教程中重點(diǎn)討論這一塊,并且通過協(xié)程間通信來重寫這段代碼。
目前為止,我們僅僅演示了 Go 語言協(xié)程的啟動(dòng)和簡(jiǎn)單使用,但是通過上述代碼還不足以驗(yàn)證協(xié)程是并發(fā)執(zhí)行的,接下來,我們通過下面這段代碼來驗(yàn)證協(xié)程的并發(fā)執(zhí)行:
package main
import (
"fmt"
"time"
)
func add(a, b int) {
var c = a + b;
fmt.Printf("%d + %d = %d\n", a, b, c)
}
func main() {
for i := 0; i < 10; i++ {
go add(1, i)
}
time.Sleep(1e9)
}
很簡(jiǎn)單,我們給協(xié)程代碼外層套上一個(gè)循環(huán)就可以了,這樣一來,就同時(shí)啟動(dòng)了 10 個(gè)子協(xié)程,由于它們是并發(fā)執(zhí)行的,執(zhí)行的先后順序無法保證,所以我們就看到了這樣的打印結(jié)果:
如果你還不放心,可以在 add
函數(shù)最后添加如下這段代碼:
time.Sleep(3e9)
表示每個(gè)子協(xié)程中執(zhí)行完打印語句睡眠 3 秒再退出,如果不是并發(fā)執(zhí)行,那么肯定至多只能打印一條結(jié)果出來,但實(shí)際情況是,仍然是打印 10 條結(jié)果,并且沒有任何延遲,證明這些加法運(yùn)算是并發(fā)執(zhí)行的。
上述就是小編為大家分享的 Go協(xié)程實(shí)現(xiàn)原理和使用分析是怎樣的了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(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)容。