当通道(channel)没有指定最大长度(cap)时,它的最大长度是0,也就意味着发送操作将会阻塞,直到另一个goroutine在对应的通道上执行了接收操作,倒过来说也是成立的,接收操作将会阻塞,直到另一个goroutine在对应的通道上执行发送操作。
也就是说,基于无缓冲队列的发送和接收是同步进行的,而基于缓冲通道的原理是,如果通道没有满,发送操作会立刻完成,如果通道内还有消息,接收操作也会立刻完成,否则,它们才会阻塞。
创建缓冲通道的方法
make(chan int, 3)
上面的代码创建了一个chan int
类型的、cap为3的缓冲通道。
通过 cap(ch)
可以获取通道的最大长度,通过len(ch)
可以获取通道中消息的数量。
下面来看一个经典的生产着与消费者的例子
func consume(ch <-chan int) {
for x := range ch {
time.Sleep(time.Second * 2)
fmt.Printf("consume %d at %v\n", x, time.Now())
}
}
func produce(ch chan<- int) {
for i := 0; i < 20; i++ {
ch <- i
fmt.Printf("produce %d at %v\n", i, time.Now())
}
}
func main() {
var blockQueue = make(chan int, 3)
go consume(blockQueue)
go consume(blockQueue)
go consume(blockQueue)
produce(blockQueue)
}
输出结果如下
produce 0 at 2021-11-10 11:35:43.4634 +0800 CST m=+0.000095155
produce 1 at 2021-11-10 11:35:43.463613 +0800 CST m=+0.000307367
produce 2 at 2021-11-10 11:35:43.463618 +0800 CST m=+0.000312590
produce 3 at 2021-11-10 11:35:43.463621 +0800 CST m=+0.000315444
produce 4 at 2021-11-10 11:35:43.463623 +0800 CST m=+0.000317893
produce 5 at 2021-11-10 11:35:43.463626 +0800 CST m=+0.000320241
consume 2 at 2021-11-10 11:35:45.467221 +0800 CST m=+2.003891597
consume 1 at 2021-11-10 11:35:45.46725 +0800 CST m=+2.003920815
consume 0 at 2021-11-10 11:35:45.467194 +0800 CST m=+2.003865144
produce 6 at 2021-11-10 11:35:45.467431 +0800 CST m=+2.004101556
produce 7 at 2021-11-10 11:35:45.467519 +0800 CST m=+2.004189837
produce 8 at 2021-11-10 11:35:45.467532 +0800 CST m=+2.004203272
consume 4 at 2021-11-10 11:35:47.472159 +0800 CST m=+4.008806751
consume 3 at 2021-11-10 11:35:47.472227 +0800 CST m=+4.008873837
produce 9 at 2021-11-10 11:35:47.472252 +0800 CST m=+4.008899632
consume 5 at 2021-11-10 11:35:47.472196 +0800 CST m=+4.008842888
produce 10 at 2021-11-10 11:35:47.472335 +0800 CST m=+4.008982669
produce 11 at 2021-11-10 11:35:47.472393 +0800 CST m=+4.009040183
consume 8 at 2021-11-10 11:35:49.472462 +0800 CST m=+6.009085066
produce 12 at 2021-11-10 11:35:49.472487 +0800 CST m=+6.009110372
consume 6 at 2021-11-10 11:35:49.47247 +0800 CST m=+6.009093455
produce 13 at 2021-11-10 11:35:49.472509 +0800 CST m=+6.009131934
consume 7 at 2021-11-10 11:35:49.472481 +0800 CST m=+6.009104714
produce 14 at 2021-11-10 11:35:49.472526 +0800 CST m=+6.009149533
consume 11 at 2021-11-10 11:35:51.473476 +0800 CST m=+8.010075752
produce 15 at 2021-11-10 11:35:51.47353 +0800 CST m=+8.010129885
consume 9 at 2021-11-10 11:35:51.473534 +0800 CST m=+8.010133276
produce 16 at 2021-11-10 11:35:51.473595 +0800 CST m=+8.010194908
consume 10 at 2021-11-10 11:35:51.473523 +0800 CST m=+8.010122515
produce 17 at 2021-11-10 11:35:51.473615 +0800 CST m=+8.010214367
consume 14 at 2021-11-10 11:35:53.478403 +0800 CST m=+10.014978929
consume 12 at 2021-11-10 11:35:53.478446 +0800 CST m=+10.015022028
produce 18 at 2021-11-10 11:35:53.478453 +0800 CST m=+10.015028340
produce 19 at 2021-11-10 11:35:53.478477 +0800 CST m=+10.015053279
consume 13 at 2021-11-10 11:35:53.478425 +0800 CST m=+10.015001341
从输出可以看出来,由于通道的cap是3,并且有三个消费者,所以生产者可以很快地生产6个消息而无需阻塞(每个消费者各拿走1个,剩下的3个占满通道)。
此时,通道已满,消费者需要2秒钟才能消费完一个消息。2秒钟以后,三个消费者开始下次消费,由于通道有3个消息,所以它们可以无阻塞地各取一个,此时通道有了空间,生产者可以继续生产,直到把通道全部占满,所以生产者又生产了3个消息。
以此重复,直到主线程的消费者退出,main函数结束。
发送操作在通道的尾部插入一个元素,接收者从通道的头部移除另一个元素,符合队列这个数据结构的特征,再加上它会伴随着阻塞,所以这种实现在java中被称为阻塞队列。很显然,在golang中的实现更简单。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://laozhu.blog.csdn.net/article/details/121323990
内容来源于网络,如有侵权,请联系作者删除!