Go無緩沖通道(同步通道)的實(shí)現(xiàn)
無緩沖的通道又稱為阻塞的通道,我們來看一下如下代碼片段。
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println("發(fā)送成功")
}
上面這段代碼能夠通過編譯,但是執(zhí)行的時(shí)候會(huì)出現(xiàn)以下錯(cuò)誤:

deadlock表示我們程序中的 goroutine 都被掛起導(dǎo)致程序死鎖了。為什么會(huì)出現(xiàn)deadlock錯(cuò)誤呢?
因?yàn)槲覀兪褂胏h := make(chan int)創(chuàng)建的是無緩沖的通道,無緩沖的通道只有在有接收方能夠接收值的時(shí)候才能發(fā)送成功,否則會(huì)一直處于等待發(fā)送的階段。
上面的代碼會(huì)阻塞在ch <- 1這一行代碼形成死鎖,那如何解決這個(gè)問題呢?
其中一種可行的方法是創(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)開了一個(gè)go協(xié)程從管道中讀去數(shù)據(jù)了,為什么還會(huì)報(bào)錯(cuò)呢?
因?yàn)楫?dāng)程序執(zhí)行到 ch <- 1時(shí),進(jìn)已經(jīng)發(fā)生了阻塞,下面的go協(xié)程還沒有來得及啟動(dòng)。
go的channel在執(zhí)行寫入時(shí)會(huì)先檢查在此之前有沒有讀取數(shù)據(jù)的一方已經(jīng)存在,在讀取時(shí)會(huì)先檢查在此之前有沒有寫入方已經(jīng)存在。
當(dāng)我們將讀的協(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()
v := <-ch
fmt.Println(v)
}()
ch <- 1
close(ch)
wg.Wait()
}

同理,如果對一個(gè)無緩沖通道執(zhí)行接收操作時(shí),沒有任何向通道中發(fā)送值的操作那么也會(huì)導(dǎo)致接收操作阻塞。
package main
import "fmt"
func main() {
ch := make(chan int)
<-ch
fmt.Println("接收成功")
}

其中一種可行的方法是創(chuàng)建一個(gè) goroutine 去寫入值,例如:
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é)程還沒有來得及啟動(dòng)。
當(dāng)我們將寫的協(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()
}

使用無緩沖通道進(jìn)行通信將導(dǎo)致發(fā)送和接收的 goroutine 同步化。因此,無緩沖通道也被稱為同步通道。
同步:兩個(gè)goroutine1(寫入方)、goroutine2(讀取方),
goroutine1先執(zhí)行,如果想要再次發(fā)送(寫入)數(shù)據(jù)的話,必須等待goroutine2將channel中的數(shù)據(jù)取出來(讀取)之后,才能再次發(fā)送。
goroutine2先執(zhí)行,如果想要再次接收數(shù)據(jù)的話,必須等待goroutine1再次向channel中寫入數(shù)據(jù)之后,才能再次接收。
執(zhí)行順序(goroutine1,goroutine2,goroutine1,goroutine2…)goroutine1和goroutine2交替執(zhí)行。
示例演示:使用一個(gè)無緩沖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無緩沖通道(同步通道)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go無緩沖通道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
B站新一代 golang規(guī)則引擎gengine基礎(chǔ)語法
這篇文章主要為大家介紹了B站新一代 golang規(guī)則引擎gengine基礎(chǔ)語法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Golang中Gin數(shù)據(jù)庫表名前綴的三種方法
本文主要介紹了Golang中Gin數(shù)據(jù)庫表名前綴的三種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02
Golang標(biāo)準(zhǔn)庫time包日常用法小結(jié)
本文主要介紹了Golang標(biāo)準(zhǔn)庫time包日常用法小結(jié),可以通過它們來獲取當(dāng)前時(shí)間、創(chuàng)建指定時(shí)間、解析時(shí)間字符串、控制時(shí)間間隔等操作,感興趣的可以了解一下2023-11-11

