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

Go語言?Channel通道詳解

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

一、通道介紹

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

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

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

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

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

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

1、聲明通道

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

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

所以通道只能傳輸一種類型的數(shù)據(jù),比如 chan int 或者 chan string,所有的類型都可以用于通道,空接口 interface{} 也可以。甚至可以(有時非常有用)創(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)建一個整型類型的通道
ch2 := make(chan interface{})         //創(chuàng)建一個空接口類型的通道, 可以存放任意格式
 
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip)             //創(chuàng)建Equip指針類型的通道, 可以存放*Equip

二、channel操作

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

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

定義一個通道:

ch := make(chan int)

1、發(fā)送

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

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

2、接收

從一個通道中接收值。

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

3、關(guān)閉

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

close(ch)

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

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

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

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

三、無緩沖通道

無緩沖的通道又稱為阻塞的通道

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

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

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

上面的代碼會阻塞在ch <- 10這一行代碼形成死鎖,那如何解決這個問題呢?

一種方法是啟用一個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ā)送成功")
}

因為我們使用ch := make(chan int)創(chuàng)建的是無緩沖的通道,無緩沖的通道只有在有人接收值的時候才能發(fā)送值。

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

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

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

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

四、有緩沖的通道

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

我們可以在使用make函數(shù)初始化通道的時候為其指定通道的容量

1、有緩沖通道聲明

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

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

2、阻塞條件

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

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

為什么對通道要限制長度而不提供無限長度的通道?

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

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

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

上面的代碼一個一個地去讀取信道簡直太費事了,Go語言允許我們使用range來讀取信道:

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

如果你執(zhí)行了上面的代碼,會報死鎖錯誤的,原因是range不等到信道關(guān)閉是不會結(jié)束讀取的。也就是如果 緩沖信道干涸了,那么range就會阻塞當(dāng)前goroutine, 所以死鎖咯。那么,我們試著避免這種情況,比較容易想到的是讀到信道為空的時候就結(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ù)據(jù),這個例子 是因為我們只在ch中存了數(shù)據(jù),現(xiàn)在一個一個往外取,信道大小是遞減的。另一個方式是顯式地關(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)閉的信道會禁止數(shù)據(jù)流入, 是只讀的。我們?nèi)匀豢梢詮年P(guān)閉的信道中取出數(shù)據(jù),但是不能再寫入數(shù)據(jù)了。

六、關(guān)閉通道

可以通過內(nèi)置的close()函數(shù)關(guān)閉channel(如果你的管道不往里存值或者取值的時候一定記得關(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)通過通道發(fā)送有限的數(shù)據(jù)時,我們可以通過close函數(shù)關(guān)閉通道來告知從該通道接收值的goroutine停止等待。

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

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    
    // 開啟goroutine將0~100的數(shù)發(fā)送到ch1中
    go func() {
        for i := 0; i < 100; i++ {
            ch1 <- i
        }
        close(ch1)
    }()
    
    // 開啟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)閉后會退出for range循環(huán)
        fmt.Println(i)
    }
}

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

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

七、單向通道

有的時候我們會將通道作為參數(shù)在多個任務(wù)函數(shù)間傳遞,很多時候我們在不同的任務(wù)函數(shù)中使用通道都會對其進(jìn)行限制,比如限制通道在函數(shù)中只能發(fā)送或只能接收。

var 通道實例 chan<- 元素類型    // 只能發(fā)送通道
var 通道實例 <-chan 元素類型    // 只能接收通道
//往通道中寫
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語言 Channel通道詳解的文章就介紹到這了,更多相關(guān)Go Channel通道內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang實現(xiàn)多存儲驅(qū)動設(shè)計SDK案例

    Golang實現(xiàn)多存儲驅(qū)動設(shè)計SDK案例

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

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

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

    一文教你打造一個簡易的Golang日志庫

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

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

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

    Go語言小白入門刷題打印輸出沙漏

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

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

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

    深入了解Golang為什么需要超時控制

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

    VSCode Golang dlv調(diào)試數(shù)據(jù)截斷問題及處理方法

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

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

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

    GO語言的IO方法實例小結(jié)

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

最新評論