关于channel的一些坑

channel可以分为无缓冲和带缓冲的通道

无缓冲的,必须要一个协程发送,一个协程接收

package main

func main() {
	var ch = make(chan int)
	ch <- 1
	<- ch
}

在主协程main,执行到 ch <- 1,即发送完消息就阻塞了。

下一句 <- ch 永远无法执行,因为所有goroutines都睡着了(即没有协程接收) - 死锁!!!

上面的那段程序便是一个明显的错误样例。在main函数执行到ch <- 1的时候main(也是一个goroutine)便已挂起,而并没有其他goroutine负责接收消息,而下面一句 <-ch 永远无法执行,系统便自动判为timeout返回error。
这种所有线程或者进程都在等待资源释放的情况,我们便把它称之为死锁。


作者:Solonk8
链接:https://www.jianshu.com/p/147bd63801b6
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

假如是带缓冲的,如下

package main

func main() {
	var ch = make(chan int, 1)
	ch <- 1
	<- ch
}  

则不会报错,因为main goroutine并没有被阻塞,带缓冲1

 

如果连续发两个,则会报错,如下:

package main

func main() {
	var ch = make(chan int, 1)
	ch <- 1
	ch <- 1
	<- ch
}

 

但如果你及时接收了,则又不会报错,如下:

package main

func main() {
	var ch = make(chan int, 1)
	ch <- 1
	<- ch
	ch <- 1
	<- ch
}

 

又或者在main goroutine里面再起一个goroutine,如下:

package main

func say(ch chan int) {
	ch <- 1
}

func main() {
	ch := make(chan int)
	go say(ch)    // 新起协程,main goroutine并没有被阻塞
	<- ch
}

  

 通道接收报错的情况:而且try-catch捕捉也无法避免crash

package main

import (
    "fmt"
)

func recive(ch chan int) {
    defer func() { // 必须要先声明defer,否则不能捕获到panic异常
        if err := recover(); err != nil {
            fmt.Println("error---------")
        }
    }()
    for {
        a := <-ch
        fmt.Println(a)
    }
}

func main() {
    ch := make(chan int, 1024)
    go recive(ch)
    done := make(chan struct{})
    <-done
}

 

对于无缓冲的信道来说,我们默认信道的发消息(send)和收消息(receive)都是阻塞(block)的。

换句话来说,无缓冲的信道在收消息和发消息的时候,goroutine都处于挂起状态。除非另一端准备好,否则goroutine无法继续往下执行。  

 

posted @ 2018-10-09 12:05  天之草  阅读(1212)  评论(0)    收藏  举报