β

Golang channel

行者无疆 6 阅读

1、什么是 channel(管道/通道)?

在 golang 中,channel 是多个 goroutine (线程) 之间传递和同步数据的一种手段。同一时刻,仅有一个线程可以向 channel 发送数据,同样的,同一时刻也只能有一个线程能从 channel 读取数据。

channel 的特性使得它可以解决并发编程可能造成的死锁问题,同时也是 Mutex (上锁)及 sync 多线程编程的替代方案,可以简化代码结构。

2、channel 的声明方式

var c chan int  // var 关键字声明,缓冲int类型数据  
var c chan string // 缓冲string类型数据  
c := make(chan int) //  make关键字声明,非缓冲管道  
c := make(chan int, 10) // 缓冲管道  
type ChanInt chan int // 设置别名  

总结为:

3、缓冲 channel 和无缓冲 channel 区别?

channel1 := make(chan int) // 无缓冲  
channel2 := make(chan int, 2) // 有缓冲  
len(channel1)   // 长度  
cap(channel2)   // 容量  

无缓冲: 缓冲为1,最多只能缓冲1个数据。无缓冲的读写实际为同步操作,只有当写与读操作匹配时,才会继续,否则会死锁!所以不能同时将读写置于主线程,如果主线程中有读/写操作,必须置于分线程的声明后,以下为三种正确的写法:

c := make(chan int)

// 1
go func() {  
    <-
}
c <- 1

// 2
go func() {  
    c <- 1
}
<-c

// 3
go func() {  
    c <- 1
}
go func() {  
    <-c
}
  
非缓冲的读和写操作必须“相遇”才能执行,否则会**阻塞**。就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走

有缓冲: 最大缓冲等于容量,相当于一个先进先出的队列。有缓冲的管道,只有当管道容量已存满,再未取走值的情况下继续放值时才会发生阻塞。

4、巧用 channel

通过 channel 的某些奇技淫巧,可以替代 sync ,简化代码量,如下示例:

1、 在一个非缓冲管道中,依次循环存入值,并逐个取出

sync 实现方法:

package main

import (  
    "fmt"
    "sync"
)

var wg sync.WaitGroup //队列

func main() {

    // 先创建一个非缓冲管道
    c := make(chan int)
    wg.Add(2)
    go func() { //子线程
        for i := 0; i < 10; i++ {
            c <- i //写入数据
            fmt.Println("写入数据")
        }
        wg.Done()
    }()

    go func() {
        for i := 0; i < 10; i++ {
            a := <-c
            fmt.Println(a) //读取数据
            fmt.Println("读取数据")
        }
        wg.Done()
    }()

    wg.Wait()

}

=> 使用 channel 实现:

package main

import "fmt"

func main() {  
    c := make(chan int)
    go func() {
        for i:=10; i<10; i++ {
            fmt.Println("------")
            c <- i
        }
        close(c)
    }()

    for n := range c {
        fmt.Println("+++++")
        fmt.Println(n)
    }
}

注: for n := range n close(c) 配合使用,成对出现。 for 循环会监视管道c,一旦管道中有数据,立即执行循环中的命令,取出值,直到 close(c) 命令关闭管道,循环结束

2、 等待多个分线程执行完毕再关闭通道——巧用多个 channel

sync 实现方法:

package main

import (  
    "fmt"
    "sync"
)

func main() {

    c := make(chan int) //创建通道

    var wg sync.WaitGroup //队列
    wg.Add(2)
    go func() {
        fmt.Println("111111111")

        for i := 0; i < 10; i++ {
            c <- i
        }
        wg.Done()
    }()

    go func() {
        fmt.Println("22222222")
        for i := 10; i < 20; i++ {
            c <- i
        }
        wg.Done()
    }()

    go func() {
        fmt.Println("33333333")
        wg.Wait()
        close(c)
    }()

    for n := range c {

        // <-c  有返回值
        fmt.Println(n)
    }
}

=> channel 解决方案:

package main

import (  
    "fmt"
)

func main() {

    c := make(chan int)
    done := make(chan bool)

    go func() {
        for i := 0; i < 10; i++ {
            c <- i
        }
        done <- true
    }()

    go func() {
        for i := 0; i < 10; i++ {
            c <- i
        }
        done <- true
    }()

    go func() {
       <-done //死锁
       <-done
       close(c)
    }

    for n := range c {
        fmt.Println(n)
    }
}
作者:行者无疆
Welcome to blog
原文地址:Golang channel, 感谢原作者分享。

发表评论