管道通信C语言,Go语言通道(chan)——goroutine之间通信的管道
如果說 goroutine 是 Go語言程序的并發(fā)體的話,那么 channels 就是它們之間的通信機(jī)制。一個(gè) channels 是一個(gè)通信機(jī)制,它可以讓一個(gè) goroutine 通過它給另一個(gè) goroutine 發(fā)送值信息。每個(gè) channel 都有一個(gè)特殊的類型,也就是 channels 可發(fā)送數(shù)據(jù)的類型。一個(gè)可以發(fā)送 int 類型數(shù)據(jù)的 channel 一般寫為 chan int。
Go語言提倡使用通信的方法代替共享內(nèi)存,當(dāng)一個(gè)資源需要在 goroutine 之間共享時(shí),通道在 goroutine 之間架起了一個(gè)管道,并提供了確保同步交換數(shù)據(jù)的機(jī)制。聲明通道時(shí),需要指定將要被共享的數(shù)據(jù)的類型。可以通過通道共享內(nèi)置類型、命名類型、結(jié)構(gòu)類型和引用類型的值或者指針。
這里通信的方法就是使用通道(channel),如下圖所示。
圖:goroutine 與 channel 的通信
在地鐵站、食堂、洗手間等公共場所人很多的情況下,大家養(yǎng)成了排隊(duì)的習(xí)慣,目的也是避免擁擠、插隊(duì)導(dǎo)致的低效的資源使用和交換過程。代碼與數(shù)據(jù)也是如此,多個(gè) goroutine 為了爭搶數(shù)據(jù),勢必造成執(zhí)行的低效率,使用隊(duì)列的方式是最高效的,channel 就是一種隊(duì)列一樣的結(jié)構(gòu)。
通道的特性
Go語言中的通道(channel)是一種特殊的類型。在任何時(shí)候,同時(shí)只能有一個(gè) goroutine 訪問通道進(jìn)行發(fā)送和獲取數(shù)據(jù)。goroutine 間通過通道就可以通信。
通道像一個(gè)傳送帶或者隊(duì)列,總是遵循先入先出(First In First Out)的規(guī)則,保證收發(fā)數(shù)據(jù)的順序。
聲明通道類型
通道本身需要一個(gè)類型進(jìn)行修飾,就像切片類型需要標(biāo)識元素類型。通道的元素類型就是在其內(nèi)部傳輸?shù)臄?shù)據(jù)類型,聲明如下:
var 通道變量 chan 通道類型
通道類型:通道內(nèi)的數(shù)據(jù)類型。
通道變量:保存通道的變量。
chan 類型的空值是 nil,聲明后需要配合 make 后才能使用。
創(chuàng)建通道
通道是引用類型,需要使用 make 進(jìn)行創(chuàng)建,格式如下:
通道實(shí)例 := make(chan 數(shù)據(jù)類型)
數(shù)據(jù)類型:通道內(nèi)傳輸?shù)脑仡愋汀?/p>
通道實(shí)例:通過make創(chuàng)建的通道句柄。
請看下面的例子:
ch1 := make(chan int) // 創(chuàng)建一個(gè)整型類型的通道
ch2 := make(chan interface{}) // 創(chuàng)建一個(gè)空接口類型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip) // 創(chuàng)建Equip指針類型的通道, 可以存放*Equip
使用通道發(fā)送數(shù)據(jù)
通道創(chuàng)建后,就可以使用通道進(jìn)行發(fā)送和接收操作。
1) 通道發(fā)送數(shù)據(jù)的格式
通道的發(fā)送使用特殊的操作符
通道變量
通道變量:通過make創(chuàng)建好的通道實(shí)例。
值:可以是變量、常量、表達(dá)式或者函數(shù)返回值等。值的類型必須與ch通道的元素類型一致。
2) 通過通道發(fā)送數(shù)據(jù)的例子
使用 make 創(chuàng)建一個(gè)通道后,就可以使用
// 創(chuàng)建一個(gè)空接口通道
ch := make(chan interface{})
// 將0放入通道中
ch
// 將hello字符串放入通道中
ch
3) 發(fā)送將持續(xù)阻塞直到數(shù)據(jù)被接收
把數(shù)據(jù)往通道中發(fā)送時(shí),如果接收方一直都沒有接收,那么發(fā)送操作將持續(xù)阻塞。Go 程序運(yùn)行時(shí)能智能地發(fā)現(xiàn)一些永遠(yuǎn)無法發(fā)送成功的語句并做出提示,代碼如下:
package main
func main() {
// 創(chuàng)建一個(gè)整型通道
ch := make(chan int)
// 嘗試將0通過通道發(fā)送
ch
}
運(yùn)行代碼,報(bào)錯:
fatal error: all goroutines are asleep - deadlock!
報(bào)錯的意思是:運(yùn)行時(shí)發(fā)現(xiàn)所有的 goroutine(包括main)都處于等待 goroutine。也就是說所有 goroutine 中的 channel 并沒有形成發(fā)送和接收對應(yīng)的代碼。
使用通道接收數(shù)據(jù)
通道接收同樣使用
① 通道的收發(fā)操作在不同的兩個(gè) goroutine 間進(jìn)行。
由于通道的數(shù)據(jù)在沒有接收方處理時(shí),數(shù)據(jù)發(fā)送方會持續(xù)阻塞,因此通道的接收必定在另外一個(gè) goroutine 中進(jìn)行。
② 接收將持續(xù)阻塞直到發(fā)送方發(fā)送數(shù)據(jù)。
如果接收方接收時(shí),通道中沒有發(fā)送方發(fā)送數(shù)據(jù),接收方也會發(fā)生阻塞,直到發(fā)送方發(fā)送數(shù)據(jù)為止。
③ 每次接收一個(gè)元素。
通道一次只能接收一個(gè)數(shù)據(jù)元素。
通道的數(shù)據(jù)接收一共有以下 4 種寫法。
1) 阻塞接收數(shù)據(jù)
阻塞模式接收數(shù)據(jù)時(shí),將接收變量作為
data :=
執(zhí)行該語句時(shí)將會阻塞,直到接收到數(shù)據(jù)并賦值給 data 變量。
2) 非阻塞接收數(shù)據(jù)
使用非阻塞方式從通道接收數(shù)據(jù)時(shí),語句不會發(fā)生阻塞,格式如下:
data, ok :=
data:表示接收到的數(shù)據(jù)。未接收到數(shù)據(jù)時(shí),data 為通道類型的零值。
ok:表示是否接收到數(shù)據(jù)。
非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要實(shí)現(xiàn)接收超時(shí)檢測,可以配合 select 和計(jì)時(shí)器 channel 進(jìn)行,可以參見后面的內(nèi)容。
3) 接收任意數(shù)據(jù),忽略接收的數(shù)據(jù)
阻塞接收數(shù)據(jù)后,忽略從通道返回的數(shù)據(jù),格式如下:
執(zhí)行該語句時(shí)將會發(fā)生阻塞,直到接收到數(shù)據(jù),但接收到的數(shù)據(jù)會被忽略。這個(gè)方式實(shí)際上只是通過通道在 goroutine 間阻塞收發(fā)實(shí)現(xiàn)并發(fā)同步。
使用通道做并發(fā)同步的寫法,可以參考下面的例子:
package main
import (
"fmt"
)
func main() {
// 構(gòu)建一個(gè)通道
ch := make(chan int)
// 開啟一個(gè)并發(fā)匿名函數(shù)
go func() {
fmt.Println("start goroutine")
// 通過通道通知main的goroutine
ch
fmt.Println("exit goroutine")
}()
fmt.Println("wait goroutine")
// 等待匿名goroutine
fmt.Println("all done")
}
執(zhí)行代碼,輸出如下:
wait goroutine
start goroutine
exit goroutine
all done
代碼說明如下:
第 10 行,構(gòu)建一個(gè)同步用的通道。
第 13 行,開啟一個(gè)匿名函數(shù)的并發(fā)。
第 18 行,匿名 goroutine 即將結(jié)束時(shí),通過通道通知 main 的 goroutine,這一句會一直阻塞直到 main 的 goroutine 接收為止。
第 27 行,開啟 goroutine 后,馬上通過通道等待匿名 goroutine 結(jié)束。
4) 循環(huán)接收
通道的數(shù)據(jù)接收可以借用 for range 語句進(jìn)行多個(gè)元素的接收操作,格式如下:
for data := range ch {
}
通道 ch 是可以進(jìn)行遍歷的,遍歷的結(jié)果就是接收到的數(shù)據(jù)。數(shù)據(jù)類型就是通道的數(shù)據(jù)類型。通過 for 遍歷獲得的變量只有一個(gè),即上面例子中的 data。
遍歷通道數(shù)據(jù)的例子請參考下面的代碼。
使用 for 從通道中接收數(shù)據(jù):
package main
import (
"fmt"
"time"
)
func main() {
// 構(gòu)建一個(gè)通道
ch := make(chan int)
// 開啟一個(gè)并發(fā)匿名函數(shù)
go func() {
// 從3循環(huán)到0
for i := 3; i >= 0; i-- {
// 發(fā)送3到0之間的數(shù)值
ch
// 每次發(fā)送完時(shí)等待
time.Sleep(time.Second)
}
}()
// 遍歷接收通道數(shù)據(jù)
for data := range ch {
// 打印通道數(shù)據(jù)
fmt.Println(data)
// 當(dāng)遇到數(shù)據(jù)0時(shí), 退出接收循環(huán)
if data == 0 {
break
}
}
}
執(zhí)行代碼,輸出如下:
3
2
1
0
代碼說明如下:
第 12 行,通過 make 生成一個(gè)整型元素的通道。
第 15 行,將匿名函數(shù)并發(fā)執(zhí)行。
第 18 行,用循環(huán)生成 3 到 0 之間的數(shù)值。
第 21 行,將 3 到 0 之間的數(shù)值依次發(fā)送到通道 ch 中。
第 24 行,每次發(fā)送后暫停 1 秒。
第 30 行,使用 for 從通道中接收數(shù)據(jù)。
第 33 行,將接收到的數(shù)據(jù)打印出來。
第 36 行,當(dāng)接收到數(shù)值 0 時(shí),停止接收。如果繼續(xù)發(fā)送,由于接收 goroutine 已經(jīng)退出,沒有 goroutine 發(fā)送到通道,因此運(yùn)行時(shí)將會觸發(fā)宕機(jī)報(bào)錯。
總結(jié)
以上是生活随笔為你收集整理的管道通信C语言,Go语言通道(chan)——goroutine之间通信的管道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 毕业论文该怎么查重,降重?
- 下一篇: 《秋池二首》第二十二句是什么
