1.关于Go语言的有缓冲通道和无缓冲通道
2.关于go语言无缓冲通道的阻塞行为
3.使用select实现channel的超时处理,使用time.After实现select的超时控制
PS: 学习自 https://haicoder.net/golang/golang-channel-select.html
//-- chan有只读channel和只写channel
//-- 无缓冲的通道:
//无缓冲的通道是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。
//如果两个 goroutine 没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。
// 创建无缓冲 channel
// c1 := make(chan int)
//-- 带缓冲的通道
// 带缓冲的通道是一种在被接收前能存储一个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。
// 创建带缓冲 channel
// c := make(chan int, 3)
//-- 使用 select 实现 channel 超时处理
//-- 在并发编程的通信过程中,经常会遇到超时问题,
//即向 channel 写数据时发现 channel 已满,或者从 channel 试图读取数据时发现 channel 为空。
//如果不正确处理这些情况,很可能会导致整个 goroutine 锁死。Go 语言 channel 超时场景:
//-- name := <-chanName
//我们从管道 chanName 里面接受数据,并将接受到的数据存放到变量 name 中,如果我们的程序一直没有给管道 chanName 发送数据,那么上面的代码就会永久的阻塞。
//那么,此时,如果没有合理的超时处理机制,这行代码很可能会导致整个进程阻塞。
func chanTest() {
ch := make(chan int)
quite := make(chan bool)
//-- 新开一个协程
go func() {
for {
//-- select 有比较多的限制,其中最重要的一条限制就是每个 case 语句里必须是一个 IO 操作。
//-- 并且,select 后面并不带判断条件,而是直接去查看 case 语句。
//-- 每个 case 语句都必须是一个面向 channel 的操作。当 select 里面有多个 case 都满足条件出发时,则 select 会随机选取一个 case 执行。
//-- select 关键字,可以同时响应多个通道的操作
select {
case num := <-ch:
//-- 如果ch里面有数据,则会往下走,如果没有数据正常会阻塞
fmt.Println("recv num = ", num)
case <-time.After(2 * time.Second):
//-- 上面的ch如果一直没数据会阻塞,那么select也会检测其他case条件
//-- 检测到后3秒超时后继续往下走
fmt.Println("TmeOut")
//-- 向管道写入数据
quite <- true
case <-time.After(5 * time.Second):
//-- 可以使用 time.After 来实现 select 的超时控制,同时,我们还可以使用 break 语句,来结束 select 语句。
fmt.Println("超时,退出循环")
quite <- true
break
}
}
}()
for i := 0; i < 1; i++ {
ch <- i
time.Sleep(time.Second)
}
//-- 这里暂时阻塞,直到可读
<-quite
fmt.Println("Process Done!")
}