Go語言如何通過通信共享內(nèi)存
介紹
Go 語言使用 goroutine
和 channel
,可以實(shí)現(xiàn)通過通信共享內(nèi)存。
本文我們介紹 Go 語言怎么通過通信共享內(nèi)存。
goroutine 和 channel
在了解 Go 語言怎么通過通信共享內(nèi)存之前。我們需要先了解一些預(yù)備知識(shí),即 goroutine
和 channel
是什么?
goroutine :
goroutine
具有簡(jiǎn)單的模型:它是與其它 goroutine
并發(fā)運(yùn)行在同一地址空間的函數(shù)。
goroutine
是輕量級(jí)的,所有消耗幾乎就只有棧空間的分配。而且棧最開始是非常小的,所以他們很廉價(jià),僅在需要時(shí)才會(huì)隨著堆空間的分配(和釋放)而變化。
摘自「Effective Go - channels[1]」。
注意:goroutine
之所以取名為 goroutine
,是因?yàn)楝F(xiàn)有的術(shù)語 - 線程、協(xié)程、進(jìn)程等等 - 無法準(zhǔn)確傳達(dá)它的含義。也有些資料將 goroutine
翻譯為 Go 協(xié)程或 Go 程。
使用 goroutine
也非常簡(jiǎn)單,在函數(shù)或方法前添加 go
關(guān)鍵字,即可在新的 goroutine
中調(diào)用它。當(dāng)調(diào)用完成后,該 goroutine
也會(huì)安靜地退出。
此外,匿名函數(shù)也可以在 goroutine
中調(diào)用。
關(guān)于 goroutine
的實(shí)現(xiàn)原理和調(diào)度器模型 GPM,感興趣的讀者朋友們可以自行查閱相關(guān)資料。
channel:
我們已了解,什么是 goroutine
,以及怎么使用 goroutine
調(diào)用函數(shù)或方法、匿名函數(shù)。
但是,想要實(shí)現(xiàn) goroutine
之間的通信,我們還需要了解 channel
。
channel
需要使用內(nèi)置函數(shù) make
分配內(nèi)存,其結(jié)果值充當(dāng)了對(duì)底層數(shù)據(jù)結(jié)構(gòu)的引用。如果提供了一個(gè)可選的參數(shù),它就會(huì)為該 channel
設(shè)置緩沖區(qū)大小,否則,該 channel
則為無緩沖區(qū)的 channel
。
關(guān)于 channel
的實(shí)現(xiàn)原理,感興趣的讀者朋友們可以閱讀「Golang 語言中的 channel 實(shí)現(xiàn)原理」。
需要注意的是,兩個(gè) goroutine
之間通過無緩沖區(qū)的 channel
通信時(shí),同步交換數(shù)據(jù)。
作為兩個(gè) goroutine
之間的通信管道,向 channel
中發(fā)送數(shù)據(jù)的 goroutine
稱為“發(fā)送者”,反之,從 channel
中接收數(shù)據(jù)的 goroutine
稱為“接收者”。
通過通信共享內(nèi)存
我們已經(jīng)基本了解 Go 語言的 goroutine
和 channel
,接下來我們看一下兩個(gè) goroutine
之間怎么使用 channel
(無緩沖區(qū)和緩沖區(qū))進(jìn)行通信?
無緩沖區(qū) channel:
示例代碼:
func main() { c := make(chan int) // 定義一個(gè)無緩沖區(qū) channel go func() { // 啟動(dòng)一個(gè) goroutine 調(diào)用匿名函數(shù) fmt.Println("啟動(dòng)一個(gè) goroutine 調(diào)用匿名函數(shù)") c <- 1 // 該 goroutine 向 channel 發(fā)送一個(gè)值(信號(hào)) }() fmt.Println("main 函數(shù)") out := <-c // main goroutine 從 channel 中接收一個(gè)值(信號(hào)),再未接收到值(信號(hào))之前,一直阻塞 fmt.Println(out) // 該打印無實(shí)際意義,僅為了讀者容易理解 }
閱讀上面這段代碼,我們定義一個(gè)無緩沖區(qū) channel
,執(zhí)行匿名函數(shù)的 goroutine
作為發(fā)送者,main goroutine
作為接收者。
需要注意的是,無緩沖區(qū) channel
,接收者在收到值之前,發(fā)送者會(huì)一直阻塞。同理,發(fā)送者在發(fā)送值之前,接收者也會(huì)一直阻塞。
緩沖區(qū) channel:
示例代碼:
func main() { // c := make(chan int) // 無緩沖區(qū) channel c := make(chan int, 5) // 緩沖區(qū) channel for i := 0; i < 20; i++ { c <- 1 go func() { fmt.Println("do something:", i) <-c }() } time.Sleep(time.Second * 2) // 為了防止 main goroutine 提前退出 }
閱讀上面這段代碼,我們定義一個(gè)緩沖區(qū)大小為 5 的 channel
,執(zhí)行匿名函數(shù)的 goroutine
作為接收者,main goroutine
作為發(fā)送者。
需要注意的是,該段代碼中有 5 個(gè)執(zhí)行匿名函數(shù)的 goroutine
,即 N 個(gè)接收者,1 個(gè)發(fā)送者(main goroutine
)。
我們前面講過,接收者在收到值之前會(huì)一直阻塞,而無緩沖區(qū) channel
在接收者收到值之前,發(fā)送者會(huì)一直阻塞。
如果我們將上面這段代碼中的緩沖區(qū) channel
換成無緩沖區(qū) channel
,N - 1
個(gè)接收者在接收到值之前,發(fā)送者會(huì)一直阻塞,發(fā)送者阻塞,導(dǎo)致接收者一直接收不到值,也會(huì)一直阻塞,從而導(dǎo)致死鎖。
上面這段話有些拗口,讀者朋友們可以通過運(yùn)行使用無緩沖區(qū) channel
的代碼來幫助自己理解。
我們運(yùn)行使用緩沖區(qū)大小為 5 的 channel
的代碼,發(fā)現(xiàn)代碼可以正常運(yùn)行,發(fā)送者和接收者之間不會(huì)產(chǎn)生死鎖。
這是因?yàn)榫彌_區(qū) channel
,發(fā)送者僅在值被復(fù)制到緩沖區(qū)之前阻塞,如果緩沖區(qū)已滿,發(fā)送者會(huì)一直阻塞,直到某個(gè)接收者取出一個(gè)值。
回到上面這段示例代碼中,執(zhí)行匿名函數(shù)的 N 個(gè) goroutine
作為接收者,在沒有收到 main goroutine
發(fā)送的數(shù)據(jù)之前,一直處于阻塞狀態(tài),直到作為發(fā)送者的 main goroutine
發(fā)送數(shù)據(jù)到緩沖區(qū) channel
中。
讀者朋友們?nèi)绻屑?xì)閱讀這段代碼,會(huì)發(fā)現(xiàn)上面這段代碼雖然不會(huì)產(chǎn)生死鎖,但是存在一個(gè) bug
。
解決方案可以閱讀我們之前的一篇文章「Go 語言使用 goroutine 運(yùn)行閉包的“坑”」,限于篇幅,我就不在本文中贅述了。
總結(jié)
本文我們介紹 Go 語言中,什么是 goroutine
和 channel
,其中 channel
分為無緩沖區(qū)和緩沖區(qū)。
在簡(jiǎn)單了解 goroutine
和 channel
后,我們又介紹怎么使用 channel
,實(shí)現(xiàn)兩個(gè) goroutine
之間通信。
以上就是Go語言如何通過通信共享內(nèi)存的詳細(xì)內(nèi)容,更多關(guān)于Go語言通信共享內(nèi)存的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Go語言如何實(shí)現(xiàn)并發(fā)安全的map
go語言提供的數(shù)據(jù)類型中,只有channel是并發(fā)安全的,基礎(chǔ)map并不是并發(fā)安全的,本文為大家整理了三種實(shí)現(xiàn)了并發(fā)安全的map的方案,有需要的可以參考下2023-12-12golang協(xié)程關(guān)閉踩坑實(shí)戰(zhàn)記錄
協(xié)程(coroutine)是Go語言中的輕量級(jí)線程實(shí)現(xiàn),下面這篇文章主要給大家介紹了關(guān)于golang協(xié)程關(guān)閉踩坑的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03基于Golang+Vue編寫一個(gè)手機(jī)遠(yuǎn)程控制電腦的懶人工具
這篇文章主要為大家詳細(xì)介紹了如何基于Golang+Vue編寫一個(gè)手機(jī)遠(yuǎn)程控制電腦的懶人工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-11-11Go實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)庫表轉(zhuǎn)結(jié)構(gòu)體詳解
這篇文章主要為大家介紹了Go實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)庫表轉(zhuǎn)結(jié)構(gòu)體詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Go語言中的匿名結(jié)構(gòu)體用法實(shí)例
這篇文章主要介紹了Go語言中的匿名結(jié)構(gòu)體用法,實(shí)例分析了匿名結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02Go語言使用sort包對(duì)任意類型元素的集合進(jìn)行排序的方法
這篇文章主要介紹了Go語言使用sort包對(duì)任意類型元素的集合進(jìn)行排序的方法,實(shí)例分析了sort排序所涉及的方法與相關(guān)的使用技巧,需要的朋友可以參考下2015-02-02Golang JSON的進(jìn)階用法實(shí)例講解
這篇文章主要給大家介紹了關(guān)于Golang JSON進(jìn)階用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用golang具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09