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

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

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

其中一種可行的方法是創(chuàng)建一個 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()
}

同理,因為當(dāng)程序執(zhí)行到 v := <-ch 時,進已經(jīng)發(fā)生了阻塞,下面的go協(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()
ch <- 1
}()
v := <-ch
fmt.Println(v)
close(ch)
wg.Wait()
}

使用無緩沖通道進行通信將導(dǎo)致發(fā)送和接收的 goroutine 同步化。因此,無緩沖通道也被稱為同步通道。
同步:兩個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í)行。
示例演示:使用一個無緩沖channel和兩個goroutine實現(xiàn)交替打印一個字符串。
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無緩沖通道(同步通道)的實現(xiàn)的文章就介紹到這了,更多相關(guān)Go無緩沖通道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang原生http包實現(xiàn)各種情況的get請求方式
這篇文章主要介紹了golang原生http包實現(xiàn)各種情況的get請求方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
Golang設(shè)計模式之責(zé)任鏈模式講解和代碼示例
責(zé)任鏈?zhǔn)且环N行為設(shè)計模式, 允許你將請求沿著處理者鏈進行發(fā)送, 直至其中一個處理者對其進行處理,本文就詳細給大家介紹一下Golang 責(zé)任鏈模式,文中有詳細的代碼示例,需要的朋友可以參考下2023-06-06
一文帶你揭秘Go中new()和make()函數(shù)的區(qū)別和用途
Go(或 Golang)是一種現(xiàn)代、靜態(tài)類型、編譯型的編程語言,專為構(gòu)建可擴展、并發(fā)和高效的軟件而設(shè)計,它提供了各種內(nèi)置的函數(shù)和特性,幫助開發(fā)人員編寫簡潔高效的代碼,在本博客文章中,我們將探討 new() 和 make() 函數(shù)之間的區(qū)別,了解何時以及如何有效地使用它們2023-10-10
Go語言中的goroutine和channel如何協(xié)同工作
在Go語言中,goroutine和channel是并發(fā)編程的兩個核心概念,它們協(xié)同工作以實現(xiàn)高效、安全的并發(fā)執(zhí)行,本文將詳細探討goroutine和channel如何協(xié)同工作,以及它們在并發(fā)編程中的作用和優(yōu)勢,需要的朋友可以參考下2024-04-04

