go并发之路(二)—— channel

533 阅读3分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

上文提到了go语言支持通过通信来共享内存,而不是通过共享内存来通信,但只是浅尝辄止。本文详细向读者介绍如何通过通信来共享内存。go语言基于通道来实现通信,并以此来共享内存。

Channel

虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,我们需要使用互斥量对内存进行加锁,但这种做法势必造成性能问题。

channel(通道)是可以让一个goroutine将信息传送给另一个goroutine的通信机制。

通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

 //var 变量 chan 元素类型
   var ch1 chan int   // 声明一个传递整型的通道
   var ch2 chan bool  // 声明一个传递布尔型的通道
   var ch3 chan []int // 声明一个传递int切片的通道

我们可以使用make创建通道

// 无缓冲的整型通道
unbuffered := make(chan int)

// 有缓冲的字符串通道
buffered := make(chan string, 10)

如何使用通道呢?

// 有缓冲的字符串通道
buffered := make(chan string, 10)

// 通过通道发送一个字符串 buffered <- "Gopher"
buffered <- "Gopher"
// 其实我们可以将<-理解为赋值的“=”
value := <- buffered
// 我们通过close关闭通道
close(buffered)

关于关闭通道需要注意的事情是,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。

关闭后的通道有以下特点:

    1.对一个关闭的通道再发送值就会导致panic2.对一个关闭的通道进行接收会一直获取值直到通道为空。
    3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
    4.关闭一个已经关闭的通道会导致panic

无缓冲通道和有缓冲通道的区别

又到了这两个经典的图 image.png

image.png

看起来很像一个生产者消费者模型,缓冲大小是盘子大小,无缓冲代表没有盘子,因此使用无缓冲的通道时,如果有程序往通道写入,就会阻塞在这行代码,直到另一协程读取(消费)这个通道的数据。而有缓冲的通道直到缓冲被写满之前,不会因为写入造成阻塞。同理,读者可自行思考消费无缓冲的通道,或者在缓存为0时消费有缓冲的通道会出现的情形。