Go無(wú)緩沖通道(同步通道)的實(shí)現(xiàn)
無(wú)緩沖的通道又稱(chēng)為阻塞的通道,我們來(lái)看一下如下代碼片段。
package main import "fmt" func main() { ch := make(chan int) ch <- 1 fmt.Println("發(fā)送成功") }
上面這段代碼能夠通過(guò)編譯,但是執(zhí)行的時(shí)候會(huì)出現(xiàn)以下錯(cuò)誤:
deadlock表示我們程序中的 goroutine 都被掛起導(dǎo)致程序死鎖了。為什么會(huì)出現(xiàn)deadlock錯(cuò)誤呢?
因?yàn)槲覀兪褂胏h := make(chan int)創(chuàng)建的是無(wú)緩沖的通道,無(wú)緩沖的通道只有在有接收方能夠接收值的時(shí)候才能發(fā)送成功,否則會(huì)一直處于等待發(fā)送的階段。
上面的代碼會(huì)阻塞在ch <- 1這一行代碼形成死鎖,那如何解決這個(gè)問(wèn)題呢?
其中一種可行的方法是創(chuàng)建一個(gè) goroutine 去接收值,例如:
package main import ( "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) ch <- 1 go func() { defer wg.Done() v := <-ch fmt.Println(v) }() close(ch) wg.Wait() }
我們已經(jīng)開(kāi)了一個(gè)go協(xié)程從管道中讀去數(shù)據(jù)了,為什么還會(huì)報(bào)錯(cuò)呢?
因?yàn)楫?dāng)程序執(zhí)行到 ch <- 1時(shí),進(jìn)已經(jīng)發(fā)生了阻塞,下面的go協(xié)程還沒(méi)有來(lái)得及啟動(dòng)。
go的channel在執(zhí)行寫(xiě)入時(shí)會(huì)先檢查在此之前有沒(méi)有讀取數(shù)據(jù)的一方已經(jīng)存在,在讀取時(shí)會(huì)先檢查在此之前有沒(méi)有寫(xiě)入方已經(jīng)存在。
當(dāng)我們將讀的協(xié)程先啟動(dòng),再寫(xiě)入,就可以了,代碼如下:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) go func() { defer wg.Done() v := <-ch fmt.Println(v) }() ch <- 1 close(ch) wg.Wait() }
同理,如果對(duì)一個(gè)無(wú)緩沖通道執(zhí)行接收操作時(shí),沒(méi)有任何向通道中發(fā)送值的操作那么也會(huì)導(dǎo)致接收操作阻塞。
package main import "fmt" func main() { ch := make(chan int) <-ch fmt.Println("接收成功") }
其中一種可行的方法是創(chuàng)建一個(gè) goroutine 去寫(xiě)入值,例如:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) v := <-ch go func() { defer wg.Done() ch <- 1 }() fmt.Println(v) close(ch) wg.Wait() }
同理,因?yàn)楫?dāng)程序執(zhí)行到 v := <-ch 時(shí),進(jìn)已經(jīng)發(fā)生了阻塞,下面的go協(xié)程還沒(méi)有來(lái)得及啟動(dòng)。
當(dāng)我們將寫(xiě)的協(xié)程先啟動(dòng),再讀取,就可以了,代碼如下:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ch := make(chan int) go func() { defer wg.Done() ch <- 1 }() v := <-ch fmt.Println(v) close(ch) wg.Wait() }
使用無(wú)緩沖通道進(jìn)行通信將導(dǎo)致發(fā)送和接收的 goroutine 同步化。因此,無(wú)緩沖通道也被稱(chēng)為同步通道。
同步:兩個(gè)goroutine1(寫(xiě)入方)、goroutine2(讀取方),
goroutine1先執(zhí)行,如果想要再次發(fā)送(寫(xiě)入)數(shù)據(jù)的話(huà),必須等待goroutine2將channel中的數(shù)據(jù)取出來(lái)(讀取)之后,才能再次發(fā)送。
goroutine2先執(zhí)行,如果想要再次接收數(shù)據(jù)的話(huà),必須等待goroutine1再次向channel中寫(xiě)入數(shù)據(jù)之后,才能再次接收。
執(zhí)行順序(goroutine1,goroutine2,goroutine1,goroutine2…)goroutine1和goroutine2交替執(zhí)行。
示例演示:使用一個(gè)無(wú)緩沖channel和兩個(gè)goroutine實(shí)現(xiàn)交替打印一個(gè)字符串。
package main import "fmt" func main() { ch := make(chan int) str := "hello, world" go func() { for { index, ok := <-ch if !ok { break } if index >= len(str) { close(ch) break } fmt.Printf("Goroutine1 : %c\n", str[index]) ch <- index + 1 } }() ch <- 0 for { index, ok := <-ch if !ok { break } if index >= len(str) { close(ch) break } fmt.Printf("Goroutine1 : %c\n", str[index]) ch <- index + 1 } }
到此這篇關(guān)于Go無(wú)緩沖通道(同步通道)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go無(wú)緩沖通道內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言使用buffer讀取文件的實(shí)現(xiàn)示例
本文主要介紹了Go語(yǔ)言使用buffer讀取文件的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04Mac下Vs code配置Go語(yǔ)言環(huán)境的詳細(xì)過(guò)程
這篇文章給大家介紹Mac下Vs code配置Go語(yǔ)言環(huán)境的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07golang實(shí)現(xiàn)分頁(yè)算法實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于golang實(shí)現(xiàn)分頁(yè)算法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09Go實(shí)現(xiàn)mongodb增刪改查工具類(lèi)的代碼示例
這篇文章主要給大家介紹了關(guān)于Go實(shí)現(xiàn)mongodb增刪改查工具類(lèi)的相關(guān)資料,MongoDB是一個(gè)NoSQL數(shù)據(jù)庫(kù),它提供了靈活的文檔存儲(chǔ)模型以及強(qiáng)大的查詢(xún)和操作功能,需要的朋友可以參考下2023-10-10