golang的channel机制

信道分为无缓冲信道(即unbuffered channel)和有缓冲信道(buffered channel)。对于无缓冲的信道来说,我们默认信道的发消息(send)和收消息(receive)都是阻塞(block)的。换句话来说,无缓冲的信道在收消息和发消息的时候,goroutine都处于挂起状态。除非另一端准备好,否则goroutine无法继续往下执行。

无缓冲channel

var ch = make(chan int)

func main() {
    ch <- 1
    <-ch
}

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

死锁分类:

  1. 只在单一goroutine里操作信道
  2. 串联信道中间一环挂起
  3. 非缓冲信道不成对出现

注意:并非所有不成对出现的非缓冲信道都会报错

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

func main() {
    ch := make(chan int)
    go say(ch)
}

虽然say函数挂起等待信道接收消息,但是main goroutine并没有被阻塞,在main函数返回后程序依然可以自动终止。

缓冲channel

func main() {
	ch := make(chan int, 3)
	ch<-1
	ch<-2
	ch<-3

	//close(ch)
	for value := range ch {
		fmt.Println(value)
		//if len(ch) == 0{
		//	break
		//}
	}
}

上述代码在执行完毕后会报deadlock的错误,其原因在于range不会自动检测信道是否干涸(drained),在提取完全部数据后,再次提取会使main函数挂起。
解决方案也有两种,第一种是在for循环中设置长度检测,如果信道为空则跳出循环;第二种是在接收完全部数据后关闭信道。这里值得注意的一点是,关闭状态的信道永远不会阻塞。
第二种方法揭示了信道的另一个特性:对于关闭的信道无法再接收新的数据,但是可以提取其中存留的数据。

转自:
https://www.jianshu.com/p/147bd63801b6
https://www.jianshu.com/p/082015461b6d

猜你喜欢

转载自blog.csdn.net/Lazybones_3/article/details/87729511