Go语言channel的学习 | 青训营

57 阅读2分钟

Go内建channel实现了go协程之间数据的读写相关操作。

并发在Go当中不仅仅是语法。它是一种设计模式。该模式提供了在处理常见并发问题的解决方案。因为并发需要同步 。Go并发是源自CSP模型,通过channel来实现协程的同步。Go并发哲学是:不通过共享内存来通信,而是通过通信来共享内存。

1.创建一个channel

func main() {
	c := make(chan int)
	fmt.Println(len(c), " ", cap(c))
	go func() {
		for i := 0; i < 4; i++ {
			c <- i
			fmt.Println("正在接收", len(c), " ", cap(c))
		}
		close(c)
	}()
	
    for data := range c {//等同于num:=<-c,只不过一直迭代
		fmt.Println(data)
	}
}

如图便创建了一个容纳int类型的channel,可往中间存放int类型变量,当channel已经满,再写数据便会阻塞,channel为空,取数据依然阻塞,并且最后循环读取数据,没有数据就阻塞,用range来迭代不断操作channel。

image.png

如上图形象的解释了一个有缓冲的通道如何在goroutine之间同步数据。

第一步,右侧的goroutine正从通道接收一个值。

第二步,右侧的goroutine独立完成了接收值的动作,而左侧的goroutine正在发送一个新值到通道里。

第三步,左侧的goroutine还在向通道发送新值,而右侧的goroutine正在通道接收一个值,但是这两个操作既不是同步的,也不会互相阻塞。

第四步,所有发送与接收都已完成,而通道中还有几个值,也有一些空间可以存更多的值。

关闭channel

func main() {
	//当channel已经满,再写数据便会阻塞,channel为空,取数据依然阻塞
	c := make(chan int, 3) //当前带有缓冲的channel
	fmt.Println(len(c), " ", cap(c))
	go func() {
		for i := 0; i < 4; i++ {
			c <- i
			fmt.Println("正在接收", len(c), " ", cap(c))
		}
		close(c) //防止一直读数据死锁
	}()
	for {
		//ok为true表示没关闭,false已经关闭了
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}
}

当channel关闭后可以从channel处收到数据,但是不能再写入数据了。

对于nil channel,无论读写都有问题。