Go語(yǔ)言入門學(xué)習(xí)之Channel通道詳解
前言
不同于傳統(tǒng)的多線程并發(fā)模型使用共享內(nèi)存來(lái)實(shí)現(xiàn)線程間通信的方式,go 是通過(guò) channel 進(jìn)行協(xié)程 (goroutine) 之間的通信來(lái)實(shí)現(xiàn)數(shù)據(jù)共享。
channel,就是一個(gè)管道,可以想像成 Go 協(xié)程之間通信的管道。它是一種隊(duì)列式的數(shù)據(jù)結(jié)構(gòu),遵循先入先出的規(guī)則。
通道的聲明
每個(gè)通道都只能傳遞一種數(shù)據(jù)類型的數(shù)據(jù),聲明時(shí)需要指定通道的類型。chan Type 表示 Type 類型的通道。通道的零值為 nil 。
var channel_name chan channel_types
var str chan string
通道的初始化
聲明完通道后,通道的值為 nil ,不能直接使用,使用 make 函數(shù)對(duì)通道進(jìn)行初始化操作。
channel_name = make(chan channel_type)
str = make(chan string)
或者
str := make(chan string)
發(fā)送和接收數(shù)據(jù)
發(fā)送數(shù)據(jù),把 data 數(shù)據(jù)發(fā)送到 channel_name 通道中。
channel_name <- data
接收數(shù)據(jù),從 channel_name 通道中接收數(shù)據(jù)到 value。
value := <- channel_name
func PrintFunc(c chan string) {
c <- "往通道里面?zhèn)鲾?shù)據(jù)"
}
func main() {
str := make(chan string)
fmt.Println("start")
go PrintFunc(str)
result := <-str
fmt.Println(result)
fmt.Println("end")
}發(fā)送與接收默認(rèn)是阻塞的。如果從通道接收數(shù)據(jù)沒(méi)接收完主協(xié)程是不會(huì)繼續(xù)執(zhí)行下去的。當(dāng)把數(shù)據(jù)發(fā)送到通道時(shí),會(huì)在發(fā)送數(shù)據(jù)的語(yǔ)句處發(fā)生阻塞,直到有其它協(xié)程從通道讀取到數(shù)據(jù),才會(huì)解除阻塞。與此類似,當(dāng)讀取通道的數(shù)據(jù)時(shí),如果沒(méi)有其它的協(xié)程把數(shù)據(jù)寫(xiě)入到這個(gè)通道,那么讀取過(guò)程就會(huì)一直阻塞著。
通道的關(guān)閉
對(duì)于一個(gè)已經(jīng)使用完畢的通道,我們要將其進(jìn)行關(guān)閉。對(duì)于一個(gè)已經(jīng)關(guān)閉的通道如果再次關(guān)閉會(huì)導(dǎo)致報(bào)錯(cuò)。
close(channel_name)
可以在接收數(shù)據(jù)時(shí),判斷通道是否已經(jīng)關(guān)閉,從通道讀取數(shù)據(jù)返回的第二個(gè)值表示通道是否沒(méi)被關(guān)閉,如果已經(jīng)關(guān)閉,返回值為 false ;如果還未關(guān)閉,返回值為 true 。
value, ok := <- channel_name
通道的容量與長(zhǎng)度
通道可以設(shè)置緩沖區(qū),通過(guò) make 的第二個(gè)參數(shù)指定緩沖區(qū)大小
ch := make(chan int, 100)
0:通道中不能存放數(shù)據(jù),在發(fā)送數(shù)據(jù)時(shí),必須要求立馬接收,否則會(huì)報(bào)錯(cuò)。此時(shí)的通道稱之為無(wú)緩沖通道。1:通道只能緩存一個(gè)數(shù)據(jù),若通道中已有一個(gè)數(shù)據(jù),此時(shí)再往里發(fā)送數(shù)據(jù),會(huì)造成程序阻塞。利用這點(diǎn)可以利用通道來(lái)做鎖。- 大于
1:通道中可以存放多個(gè)數(shù)據(jù),可以用于多個(gè)協(xié)程之間的通信管道,共享資源。
通過(guò) cap 函數(shù)和 len 函數(shù)獲取通道的容量和長(zhǎng)度。
func main() {
// 創(chuàng)建一個(gè)通道
c := make(chan int, 5)
fmt.Println("初始化:")
fmt.Println("cap:", cap(c))
fmt.Println("len:", len(c))
c <- 1
c <- 2
c <- 3
fmt.Println("傳入數(shù)據(jù):")
fmt.Println("cap:", cap(c))
fmt.Println("len:", len(c))
<-c
fmt.Println("取出一個(gè)數(shù):")
fmt.Println("cap:", cap(c))
fmt.Println("len:", len(c))
}緩沖通道與無(wú)緩沖通道
帶緩沖區(qū)的通道允許發(fā)送端的數(shù)據(jù)發(fā)送和接收端的數(shù)據(jù)獲取處于異步狀態(tài),就是說(shuō)發(fā)送端發(fā)送的數(shù)據(jù)可以放在緩沖區(qū)里面,可以等待接收端去獲取數(shù)據(jù),而不是立刻需要接收端去獲取數(shù)據(jù)。
不過(guò)由于緩沖區(qū)的大小是有限的,所以還是必須有接收端來(lái)接收數(shù)據(jù)的,否則緩沖區(qū)一滿,數(shù)據(jù)發(fā)送端就無(wú)法再發(fā)送數(shù)據(jù)了。
通道不帶緩沖,發(fā)送方會(huì)阻塞直到接收方從通道中接收了值。如果通道帶緩沖,發(fā)送方則會(huì)阻塞直到發(fā)送的值被拷貝到緩沖區(qū)內(nèi);如果緩沖區(qū)已滿,則意味著需要等待直到某個(gè)接收方獲取到一個(gè)值。接收方在有值可以接收之前會(huì)一直阻塞。
c := make(chan int) // 或者 c := make(chan int, 0)
緩沖通道允許通道里存儲(chǔ)一個(gè)或多個(gè)數(shù)據(jù),設(shè)置緩沖區(qū)后,發(fā)送端和接收端可以處于異步的狀態(tài)。
c := make(chan int, 3)
雙向通道和單向通道
雙向通道:既可以發(fā)送數(shù)據(jù)也可以接收數(shù)據(jù)
func main() {
// 創(chuàng)建一個(gè)通道
c := make(chan int)
// 發(fā)送數(shù)據(jù)
go func() {
fmt.Println("send: 1")
c <- 1
}()
// 接收數(shù)據(jù)
go func() {
n := <-c
fmt.Println("receive:", n)
}()
// 主協(xié)程休眠
time.Sleep(time.Millisecond)
}單向通道:只能發(fā)送或者接收數(shù)據(jù)。具體細(xì)分為只讀通道和只寫(xiě)通道。
<-chan 表示只讀通道:
// 定義只讀通道 c := make(chan string) // 定義類型 type Receiver = <-chan string var receiver Receiver = c // 或者簡(jiǎn)單寫(xiě)成下面的形式 type Receiver = <-chan int receiver := make(Receiver)
chan<- 表示只寫(xiě)通道:
// 定義只寫(xiě)通道 c := make(chan int) // 定義類型 type Sender = chan<- int var sender Sender = c // 或者簡(jiǎn)單寫(xiě)成下面的形式 type Sender = chan<- int sender := make(Sender)
package main
import (
"fmt"
"time"
)
// Sender 只寫(xiě)通道類型
type Sender = chan<- string
// Receiver 只讀通道類型
type Receiver = <-chan string
func main() {
// 創(chuàng)建一個(gè)雙向通道
var ch = make(chan string)
// 開(kāi)啟一個(gè)協(xié)程
go func() {
// 只寫(xiě)通道
var sender Sender = ch
fmt.Println("write only start:")
sender <- "Go"
}()
// 開(kāi)啟一個(gè)協(xié)程
go func() {
// 只讀通道
var receiver Receiver = ch
message := <-receiver
fmt.Println("readonly start: ", message)
}()
time.Sleep(time.Millisecond)
}遍歷通道
使用 for range 循環(huán)可以遍歷通道,但在遍歷時(shí)要確保通道是處于關(guān)閉狀態(tài),否則循環(huán)會(huì)被阻塞。
package main
import (
"fmt"
)
func loopPrint(c chan int) {
for i := 0; i < 10; i++ {
c <- i
}
// 記得要關(guān)閉通道
// 否則主協(xié)程遍歷完不會(huì)結(jié)束,而會(huì)阻塞
close(c)
}
func main() {
// 創(chuàng)建一個(gè)通道
var ch2 = make(chan int, 5)
go loopPrint(ch2)
for v := range ch2 {
fmt.Println(v)
}
}fibonacci 數(shù)列
package main
import (
"fmt"
)
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
// range 函數(shù)遍歷每個(gè)從通道接收到的數(shù)據(jù),因?yàn)?c 在發(fā)送完 10 個(gè)
// 數(shù)據(jù)之后就關(guān)閉了通道,所以這里我們 range 函數(shù)在接收到 10 個(gè)數(shù)據(jù)
// 之后就結(jié)束了。如果上面的 c 通道不關(guān)閉,那么 range 函數(shù)就不
// 會(huì)結(jié)束,從而在接收第 11 個(gè)數(shù)據(jù)的時(shí)候就阻塞了。
for i := range c {
fmt.Println(i)
}
}參考文章:
總結(jié)
到此這篇關(guān)于Go語(yǔ)言入門學(xué)習(xí)之Channel通道的文章就介紹到這了,更多相關(guān)Go語(yǔ)言Channel通道內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang中map的三種聲明定義方式實(shí)現(xiàn)
本文主要介紹了Golang中map的三種聲明定義方式實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
一文帶你使用Golang實(shí)現(xiàn)SSH客戶端
SSH?全稱為?Secure?Shell,是一種用于安全地遠(yuǎn)程登錄到網(wǎng)絡(luò)上的其他計(jì)算機(jī)的網(wǎng)絡(luò)協(xié)議,本文主要為大家詳細(xì)介紹了如何使用?Golang?實(shí)現(xiàn)?SSH?客戶端,需要的可以參考下2023-11-11
golang 實(shí)現(xiàn)interface{}轉(zhuǎn)其他類型操作
這篇文章主要介紹了golang 實(shí)現(xiàn)interface{}轉(zhuǎn)其他類型操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Go語(yǔ)言基礎(chǔ)學(xué)習(xí)之Context的使用詳解
在Go語(yǔ)言中,Context是一個(gè)非常重要的概念,它用于在不同的?goroutine?之間傳遞請(qǐng)求域的相關(guān)數(shù)據(jù),本文將深入探討Go語(yǔ)言中?Context特性和Context的高級(jí)使用方法,希望對(duì)大家有所幫助2023-05-05
golang實(shí)現(xiàn)unicode轉(zhuǎn)換為字符串string的方法
這篇文章主要介紹了golang實(shí)現(xiàn)unicode轉(zhuǎn)換為字符串string的方法,實(shí)例分析了Go語(yǔ)言編碼轉(zhuǎn)換的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07
淺析Golang開(kāi)發(fā)中g(shù)oroutine的正確使用姿勢(shì)
很多初級(jí)的Gopher在學(xué)習(xí)了goroutine之后,在項(xiàng)目中其實(shí)使用率不高,所以這篇文章小編主要來(lái)帶大家深入了解一下goroutine的常見(jiàn)使用方法,希望對(duì)大家有所幫助2024-03-03
Golang實(shí)現(xiàn)Json分級(jí)解析及數(shù)字解析實(shí)踐詳解
你是否遇到過(guò)在無(wú)法準(zhǔn)確確定json層級(jí)關(guān)系的情況下對(duì)json進(jìn)行解析的需求呢?本文就來(lái)和大家介紹一次解析不確定的json對(duì)象的經(jīng)歷,以及遇到的問(wèn)題和解決方法2023-02-02

