Go語(yǔ)言通道之無(wú)緩沖通道
一、通道是什么?
其實(shí)無(wú)論是原子函數(shù)還是共享鎖都是通過(guò)共享內(nèi)存的方式進(jìn)行的同步、效率一般不高,而Go語(yǔ)言中則使用了通道,它是一種通過(guò)傳遞信息的方式進(jìn)行數(shù)據(jù)同步,通過(guò)發(fā)送和接收需要共享的資源,在goroutine 之間做同步??梢园淹ǖ揽醋魇荊oroutine之間的橋梁。
例1:創(chuàng)建一個(gè)通道
// 無(wú)緩沖的整型通道 unbuffered := make(chan int) // 有緩沖的字符串通道 buffered := make(chan string, 10)
通道分為有緩沖和無(wú)緩沖的通道。
創(chuàng)建一個(gè)Channel的關(guān)鍵點(diǎn):1.使用make創(chuàng)建 2.使用chan來(lái)告訴make我要?jiǎng)?chuàng)建的是通道 3.要告訴通道我要建立什么類型的通道。
例2:向通道發(fā)送值和接受值
// 有緩沖的字符串通道 buffered := make(chan string, 10) // 通過(guò)通道發(fā)送一個(gè)字符串 buffered <- "Gopher" // 從通道接收一個(gè)字符串 value := <-buffered
這個(gè)例子中創(chuàng)建了一個(gè)string類型的Channel,并向通道內(nèi)傳遞了一個(gè)“Gopher”字符串,這里是通過(guò)<-進(jìn)行傳入的,然后通過(guò)<-這個(gè)方式把值放到value當(dāng)中。
這里我的理解 <-就好比是一個(gè)賦值符號(hào),無(wú)論是把值傳遞到Channel中,還是把Channel中的值傳出來(lái),都是將右邊的值給左邊
二、通道的種類
由上面的例如1,可以看到Channel也是有多種的,分為無(wú)緩沖通道和有緩沖通道,下面就簡(jiǎn)單總結(jié)一下兩種類型的通道。
1.無(wú)緩沖通道
無(wú)緩沖的通道(unbuffered channel)是指在接收前沒(méi)有能力保存任何值的通道。這種類型的通道要求發(fā)送goroutine 和接收goroutine 同時(shí)準(zhǔn)備好,才能完成發(fā)送和接收操作。

上面的圖很好的解釋了通道和Goroutine的關(guān)系
- 1.左右兩個(gè)goroutine都沒(méi)有將手放到通道中。
- 2.左邊的Goroutine將手放到了通道中,模擬了將數(shù)據(jù)放入通道,此時(shí)goroutine會(huì)被鎖住
- 3.右邊的Goroutine也將手放到了通道中,模擬了從通道中取出數(shù)據(jù),同樣進(jìn)入了通道也會(huì)被鎖住
- 4.兩者通過(guò)通道執(zhí)行數(shù)據(jù)的交換
- 5.交換完成
- 6.兩者將手從通道中拿出,模擬了被鎖住的goroutine被釋放
下面這個(gè)程序,模擬了兩個(gè)人打網(wǎng)球,很好的模擬了兩個(gè)協(xié)程間通過(guò)channel進(jìn)行數(shù)據(jù)交換
package ChannelDemo
import (
"fmt"
"math/rand"
"sync"
"time"
)
var wg sync.WaitGroup
func init() {
rand.Seed(time.Now().UnixNano())
}
func PlayTennis() {
court := make(chan int)
wg.Add(2)
//啟動(dòng)了兩個(gè)協(xié)程,一個(gè)納達(dá)爾一個(gè)德約科維奇
go player("納達(dá)爾", court)
go player("德約科維奇", court)
//將1放到通道中,模擬開球
court <- 1
wg.Wait()
}
func player(name string, court chan int) {
defer wg.Done()
for {
// 將數(shù)據(jù)從通道中取出
ball, ok := <-court
if !ok {
fmt.Printf("選手 %s 勝利\n", name)
return
}
//獲取一個(gè)隨機(jī)值,如果可以整除13,就讓一個(gè)人沒(méi)有擊中,進(jìn)而關(guān)閉整個(gè)通道
n := rand.Intn(100)
if n%13 == 0 {
fmt.Printf("選手 %s 沒(méi)接到\n", name)
close(court)
return
}
//如果擊中球,就將擊球的數(shù)量+1,放回通道中
fmt.Printf("選手 %s 擊中 %d\n", name, ball)
ball++
court <- ball
}
}執(zhí)行結(jié)果(每次會(huì)有變化):
選手 納達(dá)爾 擊中 1
選手 德約科維奇 擊中 2
選手 納達(dá)爾 擊中 3
選手 德約科維奇 擊中 4
選手 納達(dá)爾 擊中 5
選手 德約科維奇 擊中 6
選手 納達(dá)爾 擊中 7
選手 德約科維奇 擊中 8
選手 納達(dá)爾 沒(méi)接到
選手 德約科維奇 勝利
ok 標(biāo)志是否為false。如果這個(gè)值是false,表示通道已經(jīng)被關(guān)閉,游戲結(jié)束。
下面這個(gè)例子,模擬里一個(gè)接力賽,也就是協(xié)程之間的傳遞的另一種形式
package ChannelDemo
import (
"fmt"
"sync"
"time"
)
var runnerWg sync.WaitGroup
func Running() {
//創(chuàng)建一個(gè)“接力棒”,也就是通道
baton := make(chan int)
runnerWg.Add(1)
//創(chuàng)建第一個(gè)跑步走
go Runner(baton)
//開始跑
baton <- 1
runnerWg.Wait()
}
func Runner(baton chan int) {
var newRunner int
//選手接過(guò)接力棒
runner := <-baton
fmt.Printf("第 %d 選手接棒 \n", runner)
//如果不是第四名選手,那么說(shuō)明比賽還在繼續(xù)
if runner != 4 {
//創(chuàng)建一名新選手
newRunner = runner + 1
fmt.Printf("第 %d 準(zhǔn)備接棒 \n", newRunner)
go Runner(baton)
}
//模擬跑步
time.Sleep(100 * time.Millisecond)
//如果第四名跑完了,就結(jié)束
if runner == 4 {
fmt.Printf("第 %d 結(jié)束賽跑 \n", runner)
runnerWg.Done()
return
}
fmt.Printf("第 %d 選手和第 %d 選手交換了接力棒 \n",
runner,
newRunner)
//選手遞出接力棒
baton <- newRunner
}運(yùn)行結(jié)果:
第 1 名選手接棒
第 2 名選手準(zhǔn)備接棒
第 1 名選手將接力棒遞給第 2 名選手
第 2 名選手接棒
第 3 名選手準(zhǔn)備接棒
第 2 名選手將接力棒遞給第 3 名選手
第 3 名選手接棒
第 4 名選手準(zhǔn)備接棒
第 3 名選手將接力棒遞給第 4 名選手
第 4 名選手接棒
第 4 名選手沖線,比賽結(jié)束
三、無(wú)緩沖通道小結(jié)
我在看例子的過(guò)程中,其實(shí)遇到的問(wèn)題在于,我沒(méi)有理解goroutine是怎么進(jìn)行交換的,我以為是goroutine有一個(gè)集合一樣的結(jié)構(gòu)在通道外面等待取數(shù)據(jù),這樣就存在我剛拿完再那的情況。就像下面這個(gè)圖顯示一樣

但是實(shí)際情況應(yīng)該像下面

Go1寫入通道鎖住的Go1、Go2讀出進(jìn)入通道鎖住Go2,只有Go1寫完Go2取完才能釋放,但是像上面第一個(gè)例子代碼,讀出之后馬上就寫入,所以對(duì)于這樣的協(xié)程其實(shí)一直是鎖住的狀態(tài)。兩個(gè)協(xié)程就通過(guò)這種方式進(jìn)行數(shù)據(jù)的傳遞。
到此這篇關(guān)于Go語(yǔ)言通道之無(wú)緩沖通道的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Go1.18?新特性之多模塊Multi-Module工作區(qū)模式
這篇文章主要介紹了Go1.18?新特性之多模塊Multi-Module工作區(qū)模式,在 Go 1.18之前,建議使用依賴模塊中的 replace 指令來(lái)處理這個(gè)問(wèn)題,從 Go 1.18開始引入了一種同時(shí)處理多個(gè)模塊的新方法,通過(guò)案例給大家詳細(xì)介紹,感興趣的朋友一起看看吧2022-04-04
Golang實(shí)現(xiàn)短網(wǎng)址/短鏈服務(wù)的開發(fā)筆記分享
這篇文章主要為大家詳細(xì)介紹了如何使用Golang實(shí)現(xiàn)短網(wǎng)址/短鏈服務(wù),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-05-05
使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作
這篇文章主要介紹了使用go的interface案例實(shí)現(xiàn)多態(tài)范式操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
GO語(yǔ)言創(chuàng)建錢包并遍歷錢包(wallet)的實(shí)現(xiàn)代碼
比特幣錢包實(shí)際上是一個(gè)密鑰對(duì),當(dāng)你安裝 一個(gè)錢包應(yīng)用,或者是使用一個(gè)比特幣客戶端來(lái)生成一個(gè)新地址是,他就會(huì)為你生成一個(gè)密鑰對(duì),今天通過(guò)本文給大家分享go語(yǔ)言遍歷錢包的相關(guān)知識(shí),一起看看吧2021-05-05
go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接
所謂長(zhǎng)連接指在一個(gè)TCP連接上可以連續(xù)發(fā)送多個(gè)數(shù)據(jù)包,在TCP連接保持期間,如果沒(méi)有數(shù)據(jù)包發(fā)送,需要雙方發(fā)檢測(cè)包以維持此連接,一般需要自己做在線維持,下面這篇文章主要給大家介紹了關(guān)于go語(yǔ)言如何使用gin庫(kù)實(shí)現(xiàn)SSE長(zhǎng)連接的相關(guān)資料,需要的朋友可以參考下2023-06-06

