首页 > 其他 > 详细

Golang---Channel

时间:2020-06-20 16:57:22      阅读:56      评论:0      收藏:0      [点我收藏+]

  摘要:今天我们来学习 Go 语言中 channel , 这是 Go 语言中非常重要的一个特性。

基础知识

  创建

  在使用 channel 之前需要使用 make 来创建一个 channel, 如果在使用之前没有使用 make, 则会造成死锁(原因在后面死锁部分进行说明)

ch := make(chan int)     //创建无缓冲的channel
ch := make(chan int, N)  //创建有缓冲的channel

  读写

N := <- ch   //读操作
ch <- N      //写操作

  分类

  无缓冲:发送和接收动作是同时发生的,如果没有 goroutine 读取(<- ch),则发送者(ch <- )会一致阻塞

  有缓冲:channel 类似一个有容量的队列,当队列满的时候发送者会阻塞,当队列空的时候接收者会阻塞

  关闭

   channel 不像文件,通常不需要去关闭它们,只有在接收者必须要知道没有更多的数据的时候,才需要发送端去关闭(只有客户端才应该去关闭),比如在 range 循环遍历中。

ch := make(chan int)
close(ch)   //关闭 channel

  关闭需要注意的几点:

1, 重复关闭 channel 会导致 panic
2, 向关闭的 channel 发送数据会导致 panic
3, 从关闭的 channel 读数据不会发生 panic, 读出 channel 中已有的数据之后, 读出的就是 channel 中值类型的默认值

  判断一个 channel 是否关闭可以使用 ok-idiom 的方式,这种方式在 map 中比较常用:

//ok-idiom 方式
val, ok := <- ch
if ok == false {
    fmt.println("closed")
}else {
    fmt.println("not closed")
}

典型用法

  goroutine 通信

技术分享图片
func TestChannel() {
    ch := make(chan int)
    go func() {
        ch <- 1
    }()
    fmt.Println(<- ch)
}
goroutine 通信

  select

技术分享图片
select {
    case v, ok := <- ch1:
        if(!ok) {
            fmt.println("ch1 channel closed!")
        }else {
            fmt.println("ch1 do something")
        }
    case v, ok := <- ch2:
        if(!ok) {
            fmt.println("ch2 channel closed!")
        }else {
            fmt.println("ch2 do something")
        }
    default:
            fmt.println("ch1 not ready and ch2 not ready")
}
select

  range

  range 可以直接取到 channel 中的值,当我们使用 range 来操作 channel 的时候, 一旦 channel 关闭,channel 内部数据读完之后循环自动结束

技术分享图片
//消费者
func consumer(ch chan int) {
    for x := range ch {
        fmt.Println(x)
        // do something with x
    }
}
//生产者
func producer(ch chan int) {
    values := make([]int, 5)
    for _, v := range values {
        ch <- v
    }
}
range

  超时控制

技术分享图片
func queryDb(ch chan int) {
    time.Sleep(time.Second)
    ch <- 100
}

func main() {
    ch := make(chan int)
    go queryDb(ch)
    t := time.NewTicker(time.Second)
    select {
    case v := <- ch:
        fmt.Println("res: ", v)
    case <- t.C:
        fmt.Println("timeout")
}
超时控制 

死锁

  死锁情况1

技术分享图片
func deadlock1() {
    ch := make(chan int)
    ch <- 1
}

func deadlock2() {
    ch := make(chan int)
    <- ch
}
deadLockCase1

  死锁分析:无缓冲信道不存储值,无论是传值还是取值都会阻塞,无缓冲信道必须同时传值和取值。

  死锁情况2

技术分享图片
func Deadlock3() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    go func() {
        ch2 <- "ch2 value"  // block point
        ch1 <- "ch1 value"
    }()

    <- ch1  //block point
    <- ch2
}
deadLockCase2

  死锁分析: 在 main goroutine 中,ch1 等待数据,而在 goroutine 中,ch2 在等待数据,所以造成死锁。

  死锁情况3

技术分享图片
func Deadlock4() {
    chs := make(chan string, 2)
    chs <- "first"
    chs <- "second"
    //close(chs) 需要发送端主动去关闭

    for ch := range chs {
        fmt.Println(ch)
    }
}
deadLockCase3

  死锁分析: 从空 channel 中读取会导致阻塞,同死锁情况1。

源码分析

 

 

参考资料:

https://tour.golang.org/concurrency/4

 

Golang---Channel

原文:https://www.cnblogs.com/zpcoding/p/13169028.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!