欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言?Channel通道詳解

 更新時(shí)間:2023年07月10日 09:36:23   作者:勤天  
Channel是一個(gè)通道,可以通過(guò)它讀取和寫(xiě)入數(shù)據(jù),它就像水管一樣,網(wǎng)絡(luò)數(shù)據(jù)通過(guò)Channel 讀取和寫(xiě)入,這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言?Channel通道的相關(guān)資料,需要的朋友可以參考下

一、通道介紹

單純地將函數(shù)并發(fā)執(zhí)行是沒(méi)有意義的。函數(shù)與函數(shù)間需要交換數(shù)據(jù)才能體現(xiàn)并發(fā)執(zhí)行函數(shù)的意義。

雖然可以使用共享內(nèi)存進(jìn)行數(shù)據(jù)交換,但是共享內(nèi)存在不同的 goroutine 中容易發(fā)生競(jìng)態(tài)問(wèn)題。為了保證數(shù)據(jù)交換的正確性,必須使用互斥量對(duì)內(nèi)存進(jìn)行加鎖,這種做法勢(shì)必造成性能問(wèn)題。

go提倡使用通信的方法代替共享內(nèi)存,這里通信的方法就是使用通道(channel),如下圖所示。

在地鐵站、食堂、洗手間等公共場(chǎng)所人很多的情況下,大家養(yǎng)成了排隊(duì)的習(xí)慣,目的也是避免擁擠、插隊(duì)導(dǎo)致的低效的資源使用和交換過(guò)程。代碼與數(shù)據(jù)也是如此,多個(gè) goroutine 為了爭(zhēng)搶數(shù)據(jù),勢(shì)必造成執(zhí)行的低效率,使用隊(duì)列的方式是最高效的,channel 就是一種隊(duì)列一樣的結(jié)構(gòu)。

Go 語(yǔ)言中的通道(channel)是一種特殊的類型。在任何時(shí)候,同時(shí)只能有一個(gè) goroutine 訪問(wèn)通道進(jìn)行發(fā)送和獲取數(shù)據(jù)。goroutine 間通過(guò)通道就可以通信。

通道像一個(gè)傳送帶或者隊(duì)列,總是遵循先入先出(First In First Out)的規(guī)則,保證收發(fā)數(shù)據(jù)的順序。

1、聲明通道

var 變量 chan 元素類型
var ch1 chan int   // 聲明一個(gè)傳遞整型的通道
var ch2 chan bool  // 聲明一個(gè)傳遞布爾型的通道
var ch3 chan []int // 聲明一個(gè)傳遞int切片的通道

chan 類型的空值是 nil,聲明后需要配合 make 后才能使用。

所以通道只能傳輸一種類型的數(shù)據(jù),比如 chan int 或者 chan string,所有的類型都可以用于通道,空接口 interface{} 也可以。甚至可以(有時(shí)非常有用)創(chuàng)建通道的通道。

2、創(chuàng)建通道

通道是引用類型,需要使用 make 進(jìn)行創(chuàng)建(分配內(nèi)存),格式如下:

var ch1 chan string
ch1 = make(chan string)
 
//或者使用短類型
ch1 := make(chan string)

示例

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

二、channel操作

通道有發(fā)送(send)、接收(receive)和關(guān)閉(close)三種操作。發(fā)送和接收都使用<-符號(hào)。

通道創(chuàng)建后,就可以使用通道進(jìn)行發(fā)送和接收操作。

定義一個(gè)通道:

ch := make(chan int)

1、發(fā)送

將一個(gè)值發(fā)送到通道中。

ch <- 10 // 把10發(fā)送到ch中

2、接收

從一個(gè)通道中接收值。

x := <- ch // 從ch中接收值并賦值給變量x
<-ch       // 從ch中接收值,忽略結(jié)果

3、關(guān)閉

我們通過(guò)調(diào)用內(nèi)置的close函數(shù)來(lái)關(guān)閉通道

close(ch)

關(guān)于關(guān)閉通道需要注意的事情是,只有在通知接收方goroutine所有的數(shù)據(jù)都發(fā)送完畢的時(shí)候才需要關(guān)閉通道

通道是可以被垃圾回收機(jī)制回收的,它和關(guān)閉文件是不一樣的,在結(jié)束操作之后關(guān)閉文件是必須要做的,但關(guān)閉通道不是必須的。

關(guān)閉后的通道有以下特點(diǎn):

  • 對(duì)一個(gè)關(guān)閉的通道再發(fā)送值就會(huì)導(dǎo)致panic。
  • 對(duì)一個(gè)關(guān)閉的通道進(jìn)行接收會(huì)一直獲取值直到通道為空。(如果通道中還有數(shù)據(jù)的話)
  • 對(duì)一個(gè)關(guān)閉的并且沒(méi)有值的通道執(zhí)行接收操作會(huì)得到對(duì)應(yīng)類型的零值。
  • 關(guān)閉一個(gè)已經(jīng)關(guān)閉的通道會(huì)導(dǎo)致panic。

三、無(wú)緩沖通道

無(wú)緩沖的通道又稱為阻塞的通道

func main() {
    ch := make(chan int)
    ch <- 10
    fmt.Println("發(fā)送成功")
}
 
//這段代碼僅作為 描述無(wú)緩沖通道,實(shí)際會(huì)形成deadlock
//具體原因,看下述分析

上面這段代碼能夠通過(guò)編譯,但是執(zhí)行的時(shí)候會(huì)出現(xiàn)以下錯(cuò)誤:

fatal error: all goroutines are asleep - deadlock!
 
goroutine 1 [chan send]:
main.main()
      main.go:8 +0x54

上面的代碼會(huì)阻塞在ch <- 10這一行代碼形成死鎖,那如何解決這個(gè)問(wèn)題呢?

一種方法是啟用一個(gè)goroutine去接收值,例如:

func recv(c chan int) {
    ret := <-c
    fmt.Println("接收成功", ret)
}
 
func main() {
    ch := make(chan int)
    go recv(ch) // 啟用goroutine從通道接收值
    ch <- 10
    fmt.Println("發(fā)送成功")
}

因?yàn)槲覀兪褂胏h := make(chan int)創(chuàng)建的是無(wú)緩沖的通道,無(wú)緩沖的通道只有在有人接收值的時(shí)候才能發(fā)送值。

無(wú)緩沖通道總結(jié):

1)無(wú)緩沖通道上的發(fā)送操作會(huì)阻塞,直到另一個(gè)goroutine在該通道上執(zhí)行接收操作,這時(shí)值才能發(fā)送成功,兩個(gè)goroutine將繼續(xù)執(zhí)行。

2)相反,如果接收操作先執(zhí)行,接收方的goroutine將阻塞,直到另一個(gè)goroutine在該通道上發(fā)送一個(gè)值。

3)使用無(wú)緩沖通道進(jìn)行通信將導(dǎo)致發(fā)送和接收的goroutine同步化。因此,無(wú)緩沖通道也被稱為同步通道。

四、有緩沖的通道

解決上面問(wèn)題的方法還有一種就是使用有緩沖區(qū)的通道。

我們可以在使用make函數(shù)初始化通道的時(shí)候?yàn)槠渲付ㄍǖ赖娜萘?/p>

1、有緩沖通道聲明

通道實(shí)例 := make(chan 通道類型, 緩沖大小)
func main() {
    ch := make(chan int, 1) // 創(chuàng)建一個(gè)容量為1的有緩沖區(qū)通道
    ch <- 10
    fmt.Println("發(fā)送成功")
}

只要通道的容量大于零,那么該通道就是有緩沖的通道,通道的容量表示通道中能存放元素的數(shù)量。

2、阻塞條件

帶緩沖通道在很多特性上和無(wú)緩沖通道是類似的。無(wú)緩沖通道可以看作是長(zhǎng)度永遠(yuǎn)為 0 的帶緩沖通道。因此根據(jù)這個(gè)特性,帶緩沖通道在下面列舉的情況下依然會(huì)發(fā)生阻塞:

  • 帶緩沖通道被填滿時(shí),嘗試再次發(fā)送數(shù)據(jù)時(shí)發(fā)生阻塞。
  • 帶緩沖通道為空時(shí),嘗試接收數(shù)據(jù)時(shí)發(fā)生阻塞。

為什么對(duì)通道要限制長(zhǎng)度而不提供無(wú)限長(zhǎng)度的通道?

我們知道通道(channel)是在兩個(gè) goroutine 間通信的橋梁。使用 goroutine 的代碼必然有一方提供數(shù)據(jù),一方消費(fèi)數(shù)據(jù)。當(dāng)提供數(shù)據(jù)一方的數(shù)據(jù)供給速度大于消費(fèi)方的數(shù)據(jù)處理速度時(shí),如果通道不限制長(zhǎng)度,那么內(nèi)存將不斷膨脹直到應(yīng)用崩潰。

因此,限制通道的長(zhǎng)度有利于約束數(shù)據(jù)提供方的供給速度,供給數(shù)據(jù)量必須在消費(fèi)方處理量+通道長(zhǎng)度的范圍內(nèi),才能正常地處理數(shù)據(jù)。

五、循環(huán)讀取信道

上面的代碼一個(gè)一個(gè)地去讀取信道簡(jiǎn)直太費(fèi)事了,Go語(yǔ)言允許我們使用range來(lái)讀取信道:

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
 
    for v := range ch {
        fmt.Println(v)
    }
}
 
//deadline

如果你執(zhí)行了上面的代碼,會(huì)報(bào)死鎖錯(cuò)誤的,原因是range不等到信道關(guān)閉是不會(huì)結(jié)束讀取的。也就是如果 緩沖信道干涸了,那么range就會(huì)阻塞當(dāng)前goroutine, 所以死鎖咯。那么,我們?cè)囍苊膺@種情況,比較容易想到的是讀到信道為空的時(shí)候就結(jié)束讀取

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
for v := range ch {
    fmt.Println(v)
    if len(ch) <= 0 { // 如果現(xiàn)有數(shù)據(jù)量為0,跳出循環(huán)
        break
    }
}

以上的方法是可以正常輸出的,但是注意檢查信道大小的方法不能在信道存取都在發(fā)生的時(shí)候用于取出所有數(shù)據(jù),這個(gè)例子 是因?yàn)槲覀冎辉赾h中存了數(shù)據(jù),現(xiàn)在一個(gè)一個(gè)往外取,信道大小是遞減的。另一個(gè)方式是顯式地關(guān)閉信道:

ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
// 顯式地關(guān)閉信道
close(ch)
for v := range ch {
    fmt.Println(v)
}

被關(guān)閉的信道會(huì)禁止數(shù)據(jù)流入, 是只讀的。我們?nèi)匀豢梢詮年P(guān)閉的信道中取出數(shù)據(jù),但是不能再寫(xiě)入數(shù)據(jù)了。

六、關(guān)閉通道

可以通過(guò)內(nèi)置的close()函數(shù)關(guān)閉channel(如果你的管道不往里存值或者取值的時(shí)候一定記得關(guān)閉管道)

package main
 
import "fmt"
 
func main() {
    c := make(chan int)
    go func() {
        for i := 0; i < 5; i++ {
            c <- i
        }
        close(c)
    }()
    for {
        if data, ok := <-c; ok {
            fmt.Println(data)
        } else {
            break
        }
    }
    fmt.Println("main結(jié)束")
}

判斷通道是否關(guān)閉?

當(dāng)通過(guò)通道發(fā)送有限的數(shù)據(jù)時(shí),我們可以通過(guò)close函數(shù)關(guān)閉通道來(lái)告知從該通道接收值的goroutine停止等待。

當(dāng)通道被關(guān)閉時(shí),往該通道發(fā)送值會(huì)引發(fā)panic,從該通道里接收的值一直都是類型零值。那如何判斷一個(gè)通道是否被關(guān)閉了呢?

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    
    // 開(kāi)啟goroutine將0~100的數(shù)發(fā)送到ch1中
    go func() {
        for i := 0; i < 100; i++ {
            ch1 <- i
        }
        close(ch1)
    }()
    
    // 開(kāi)啟goroutine從ch1中接收值,并將該值的平方發(fā)送到ch2中
    go func() {
        for {
            
            // 通道關(guān)閉后再取值ok=false
            
            i, ok := <-ch1 
            if !ok {
                break
            }
            ch2 <- i * i
        }
        close(ch2)
    }()
    
    // 在主goroutine中從ch2中接收值打印
    for i := range ch2 { // 通道關(guān)閉后會(huì)退出for range循環(huán)
        fmt.Println(i)
    }
}

說(shuō)明:在知道通道的一些阻塞情況后,為了防止deadlock ,可以使用更友好的方式從通道中讀取數(shù)據(jù)

 if i, ok := <-ch1 ;ok{
     ...
 }

七、單向通道

有的時(shí)候我們會(huì)將通道作為參數(shù)在多個(gè)任務(wù)函數(shù)間傳遞,很多時(shí)候我們?cè)诓煌娜蝿?wù)函數(shù)中使用通道都會(huì)對(duì)其進(jìn)行限制,比如限制通道在函數(shù)中只能發(fā)送或只能接收。

var 通道實(shí)例 chan<- 元素類型    // 只能發(fā)送通道
var 通道實(shí)例 <-chan 元素類型    // 只能接收通道
//往通道中寫(xiě)
func counter(out chan<- int) {
    for i := 0; i < 100; i++ {
        out <- i
    }
    close(out)
}
func squarer(out chan<- int, in <-chan int) {
    for i := range in {
        out <- i * i
    }
    close(out)
}
//從通道中讀
func printer(in <-chan int) {
    for i := range in {
        fmt.Println(i)
    }
}
func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    go counter(ch1)
    go squarer(ch2, ch1)
    printer(ch2)
}

總結(jié) 

到此這篇關(guān)于Go語(yǔ)言 Channel通道詳解的文章就介紹到這了,更多相關(guān)Go Channel通道內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang實(shí)現(xiàn)多存儲(chǔ)驅(qū)動(dòng)設(shè)計(jì)SDK案例

    Golang實(shí)現(xiàn)多存儲(chǔ)驅(qū)動(dòng)設(shè)計(jì)SDK案例

    這篇文章主要介紹了Golang實(shí)現(xiàn)多存儲(chǔ)驅(qū)動(dòng)設(shè)計(jì)SDK案例,Gocache是一個(gè)基于Go語(yǔ)言編寫(xiě)的多存儲(chǔ)驅(qū)動(dòng)的緩存擴(kuò)展組件,更多具體內(nèi)容感興趣的小伙伴可以參考一下
    2022-09-09
  • 安裝GoLang環(huán)境和開(kāi)發(fā)工具的圖文教程

    安裝GoLang環(huán)境和開(kāi)發(fā)工具的圖文教程

    Go是一門(mén)由Google開(kāi)發(fā)的編程語(yǔ)言,GoLand的安裝非常簡(jiǎn)單,本文主要介紹了安裝GoLang環(huán)境和開(kāi)發(fā)工具的圖文教程,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • 一文教你打造一個(gè)簡(jiǎn)易的Golang日志庫(kù)

    一文教你打造一個(gè)簡(jiǎn)易的Golang日志庫(kù)

    這篇文章主要為大家詳細(xì)介紹了如何使用不超過(guò)130行的代碼,通過(guò)一系列g(shù)olang的特性,來(lái)打造一個(gè)簡(jiǎn)易的golang日志庫(kù),感興趣的小伙伴可以了解一下
    2023-06-06
  • Go語(yǔ)言特點(diǎn)及基本數(shù)據(jù)類型使用詳解

    Go語(yǔ)言特點(diǎn)及基本數(shù)據(jù)類型使用詳解

    這篇文章主要為大家介紹了Go語(yǔ)言特點(diǎn)及基本數(shù)據(jù)類型使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Go語(yǔ)言小白入門(mén)刷題打印輸出沙漏

    Go語(yǔ)言小白入門(mén)刷題打印輸出沙漏

    這篇文章主要介紹了Go語(yǔ)言刷題打印輸出沙漏的示例過(guò)程詳解,非常適合剛?cè)腴T(mén)Go語(yǔ)言的小白學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • GO語(yǔ)言中err接口及defer延遲異常處理分析

    GO語(yǔ)言中err接口及defer延遲異常處理分析

    這篇文章主要為大家介紹了GO語(yǔ)言中err接口及defer延遲異常處理的示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • 深入了解Golang為什么需要超時(shí)控制

    深入了解Golang為什么需要超時(shí)控制

    本文將介紹為什么需要超時(shí)控制,然后詳細(xì)介紹Go語(yǔ)言中實(shí)現(xiàn)超時(shí)控制的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-05-05
  • VSCode Golang dlv調(diào)試數(shù)據(jù)截?cái)鄦?wèn)題及處理方法

    VSCode Golang dlv調(diào)試數(shù)據(jù)截?cái)鄦?wèn)題及處理方法

    這篇文章主要介紹了VSCode Golang dlv調(diào)試數(shù)據(jù)截?cái)鄦?wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • 使用docker構(gòu)建golang線上部署環(huán)境的步驟詳解

    使用docker構(gòu)建golang線上部署環(huán)境的步驟詳解

    這篇文章主要介紹了使用docker構(gòu)建golang線上部署環(huán)境的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • GO語(yǔ)言的IO方法實(shí)例小結(jié)

    GO語(yǔ)言的IO方法實(shí)例小結(jié)

    這篇文章主要介紹了GO語(yǔ)言的IO方法實(shí)例小結(jié),Docker的火爆促成了當(dāng)下新興的Go語(yǔ)言人氣的大幅攀升,需要的朋友可以參考下
    2015-10-10

最新評(píng)論