Golang channel

channel簡介

channel俗稱管道,用于數據傳遞或數據共享,其本質是一個先進先出的隊列,使用goroutine+channel進行數據通訊簡單高效,同時也線程安全,多個goroutine可同時修改一個channel,不需要加鎖

channel可分為三種類型:

  • 只讀channel:只能讀channel里面數據,不可寫入

  • 只寫channel:只能寫數據,不可讀

  • 一般channel:可讀可寫

channel使用

定義和聲明

var readOnlyChan <-chan int            // 只讀chan
var writeOnlyChan chan<- int           // 只寫chan
var mychan  chan int                     //讀寫channel
//定義完成以后需要make來分配內存空間,不然使用會deadlock
mychannel = make(chan int,10)

//或者
read_only := make (<-chan int,10)//定義只讀的channel
write_only := make (chan<- int,10)//定義只寫的channel
read_write := make (chan int,10)//可同時讀寫

//操作
write_only <- "wd"  //寫數據
a := <- read_only //讀取數據
a, ok := <- read_only  //優(yōu)雅的讀取數據

注:

  • 讀寫操作注意:
    • 管道如果未關閉,在讀取超時會則會引發(fā)deadlock異常
    • 管道如果關閉進行寫入數據會pannic
    • 當管道中沒有數據時候再行讀取或讀取到默認值,如int類型默認值是0
  • 循環(huán)管道注意:
    • 使用range循環(huán)管道,如果管道未關閉會引發(fā)deadlock錯誤。
    • 如果采用for死循環(huán)已經關閉的管道,當管道沒有數據時候,讀取的數據會是管道的默認值,并且循環(huán)不會退出。

示例代碼:

package main

import (
    "fmt"
    "time"
)

func main() {
    mychannel := make(chan int,10)
    for i := 0;i < 10;i++{
        mychannel <- i
    }
    close(mychannel)  //關閉管道
    fmt.Println("data lenght: ",len(mychannel))
    for  v := range mychannel {  //循環(huán)管道
        fmt.Println(v)
    }
    fmt.Printf("data lenght:  %d",len(mychannel))
}

帶緩沖區(qū)channel和不帶緩沖區(qū)channel

帶緩沖區(qū)channel:定義聲明時候制定了緩沖區(qū)大小(長度),可以保存多個數據。

ch := make(chan int ,10) //帶緩沖區(qū)

不帶緩沖區(qū)channel:只能存一個數據,并且只有當該數據被取出時候才能存下一個數據。

ch := make(chan int) //不帶緩沖區(qū)

channel實現作業(yè)池

創(chuàng)建三個channel,一個channel用于接受任務,一個channel用于保持結果,還有個channel用于決定程序退出的時候。

package main

import (
    "fmt"
)

func Task(taskch, resch chan int, exitch chan bool) {
    defer func() {   //異常處理
        err := recover()
        if err != nil {
            fmt.Println("do task error:", err)
            return
        }
    }()

    for t := range taskch { //  處理任務
        fmt.Println("do task :", t)
        resch <- t //
    }
    exitch <- true //處理完發(fā)送退出信號
}

func main() {
    taskch := make(chan int, 20) //任務管道
    resch := make(chan int, 20)  //結果管道
    exitch := make(chan bool, 5) //退出管道
    go func() {
        for i := 0; i < 10; i++ {
            taskch <- i
        }
        close(taskch)
    }()

    for i := 0; i < 5; i++ {  //啟動5個goroutine做任務
        go Task(taskch, resch, exitch)
    }

    go func() { //等5個goroutine結束
        for i := 0; i < 5; i++ {
            <-exitch
        }
        close(resch)  //任務處理完成關閉結果管道,不然range報錯
        close(exitch)  //關閉退出管道
    }()

    for res := range resch{  //打印結果
        fmt.Println("task res:",res)
    }
}

select-case實現非阻塞channel

原理通過select+case加入一組管道,當滿足(這里說的滿足意思是有數據可讀或者可寫)select中的某個case時候,那么該case返回,若都不滿足case,則走default分支。

package main

import (
    "fmt"
)

func send(c chan int)  {
    for i :=1 ; i<10 ;i++  {
    c <-i
    fmt.Println("send data : ",i)
    }
}

func main() {
    resch := make(chan int,20)
    strch := make(chan string,10)
    go send(resch)
    strch <- "wd"
    select {
    case a := <-resch:
        fmt.Println("get data : ", a)
    case b := <-strch:
        fmt.Println("get data : ", b)
    default:
        fmt.Println("no channel actvie")

    }
}

channel頻率控制

在對channel進行讀寫的時,go還提供了非常人性化的操作,那就是對讀寫的頻率控制,通過time.Ticke實現

package main

import (
    "time"
    "fmt"
)

func main(){
    requests:= make(chan int ,5)
    for i:=1;i<5;i++{
        requests<-i
    }
    close(requests)
    limiter := time.Tick(time.Second*1)
    for req:=range requests{
        <-limiter
        fmt.Println("requets",req,time.Now()) //執(zhí)行到這里,需要隔1秒才繼續(xù)往下執(zhí)行,time.Tick(timer)上面已定義
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容