channel

头像
zero
    阅读 3 分钟

    channel基本用法

    # 使用通道
    ch <- x                        // 发送语句
    x := <-ch                      // 赋值语句
    <-ch                           // 接收语句,丢弃结果
    
    # 创建通道
    ch := make(chan int)           // 无缓冲通道
    ch := make(chan int, 0)        // 无缓冲通道
    ch := make(chat int, 3)        // 容量为3的缓冲通道

    应用场景:

    • 无缓冲通道:用于发送方协程和接收方协程同步化
    • 有缓冲通道:提高性能

    单向通道类型

    Go的类型系统提供单项通道类型,仅发送或仅接收。
    需要注意的是双通道类型(即chan int这种)可以转为发送通道类型(chat<- int)、接收通道类型(int <- chan int),但单向通道类型不能转双通道

    chat<- int             // 只发送通道
    <-chat int             // 只接收通道,不能使用close将其关闭,因为close只能在发送方调用

    例子:
    image.png

    func main()  {
        naturals := make(chan int)
        squares := make(chan int)
    
        go counter(naturals)
        go squarer(naturals, squares)
        printer(squares)
    }
    
    func counter(out chan<-int)  {
        for x := 0; x < 100; x++ {
            out <- x
        }
        close(out)
    }
    
    func squarer(in<-chan int, out chan<- int)  {
        for v := range in {
            out <- v * v
        }
        close(out)
    }
    
    func printer(in <-chan int)  {
        for v := range in {
            fmt.Println(v)
        }
    }

    阻塞场景

    无缓冲通道

    特点,发送的数据被接收了,才算完成。
    阻塞场景:

    • 通道中无数据,向通道写数据,但没有其它携程读取
    • 通道中无数据,向通道读数据

    例子:

    // 情形一
    func main()  {
        c := make(chan string)        
        c <- "hello world"           // 执行到该步会一直阻塞,因为接收方和发送发是同一个携程,始终没有接收方接收,进而导致报错
        msg := <- c                  
    }
    
    // 情形二
    func main()  {
        c := make(chan string)
        fmt.Println(<-c)
    }
    
    // output
    fatal error: all goroutines are asleep - deadlock!

    有缓冲通道

    特点:有缓存空间时,发送方会向通道发送内容然后直接返回(即非阻塞),缓存中有数据时从通道中读取数据直接返回
    阻塞场景:

    • 通道已满,执行写通道,但无携程读。
    • 通道缓存无数据,但执行读通道。
    // 情形一
    func main()  {
        c := make(chan string, 2)
        c <- "hello"
        c <- "world"
        c <- "golang"
    
        fmt.Println(<-c)
    }
    
    // 情形二
    func main()  {
        c := make(chan string, 2)
    
        fmt.Println(<-c)
    }
    
    # output
    fatal error: all goroutines are asleep - deadlock!

    非阻塞写法

    无缓冲通道

    方式1:先写接受者

    func main()  {
        c := make(chan string)
    
        // 接收者
        go func() {
            v := <-c
            fmt.Println(v)
        }()
    
        // 发送者
        c <- "pig"
    }

    方式2:先写发送者

    func main()  {
        c := make(chan string)
    
        // 发送者
        go func() {
            c <- "pig"
        }()
        
        // 接收者
        fmt.Println(<-c)
    }

    有缓冲通道

    使用select解决多个协程读通道阻塞问题

    func main()  {
        c1 := make(chan string)
        c2 := make(chan string)
    
        go func() {                        // 协程1
            for {
                c1 <- "Event 1s"
                time.Sleep(1 * time.Second)
            }
        }()
    
        go func() {                        // 协程2
            for {
                c2 <- "Event 2s"
                time.Sleep(2 * time.Second)
            }
        }()
    
        for {
            // fmt.Println(<-c1)            // 如果c1读通道被阻塞,则导致c2执行不了读通道
            // fmt.Println(<-c2)            // 同理如上
            select {
            case msg1 := <-c1:
                fmt.Println(msg1)            // case默认break
            case msg2 := <-c2:
                fmt.Println(msg2)
            }
        }
    }

    zero
    6 声望0 粉丝

    « 上一篇
    SAN证书生成
    下一篇 »
    goroutine