读取和写通道超时, 是很多人在使用通道过程中经常遇到的, 下面就介绍一下通道,然后简单的对读写通道做超时处理
1. 通道采用的是FIFO(先进先出)
2. 通道的创建
chn := make(chan int, 0) // 创建读写int类型的通道, 0 代表着缓存为0,即写同时必须读取, 否则将堵塞
readOnlyChn := make(<-chan int) // 只读通道
writeOnlyChn := make(chan<- int) // 只写通道
3. 关闭通道
i. 读通道直接返回0(整数类型),bytes则返回[],
ii. 写通道 会抛出异常 (send on closed channel)
4. 判断通道是否关闭
v, ok := <-chn // ok=true说明正常,ok=false说明通道关闭
5. 通道置为nil
i. 读写通道时都会死锁
ii. select 前置 nil,则select时直接忽略,select后没有其它的case则同样堵塞
package main
import (
"fmt"
"time"
)
func recv(chn chan int) {
for {
select {
case v, ok := <-chn:
fmt.Println("value: ", v, "; ok: ", ok)
if !ok {
fmt.Println("channel closed!!! ")
return // 退出协程
}
default:
fmt.Println("recv alive print.")
time.Sleep(time.Second * 1)
}
}
}
func main() {
var chn = make(chan int, 0)
//var readOnlyChn = make(<-chan int)
//var writeOnlyChn = make(chan<- int)
/* 先启动接收协程, 在通道写入数据的时候进行读出, 由于之前创建了 0 缓存的通道, 写不同时读出造成堵塞 */
go recv(chn)
chn<-1024 // 写入1024数据
time.Sleep(time.Second * 2) // 延时两秒, 好让接收线程接收处理写入数据
chn = nil
//close(chn) // 关闭通道
time.Sleep(5 * time.Second)
}
上面的使用中, 当通道被置为nil的时候虽然由于加了default不会造成死锁, 但default依然会进行不断的打印执行;
有人说了, 可以在default时判断通道是否为nil, 思路是对的,但是却没那么简单, Go中只有值传递, 即你在传通道这个参数的时候是对通道引用的拷贝, 而不是其本身, 所以通道置nil,其实recv函数内部的chn还是非空的
这样, 不管是通道为nil还是关闭通道都能够正常监控到了, 并及时退出协程了
package main
import (
"fmt"
"time"
)
func recv(chn *chan int) {
for {
select {
case v, ok := <-*chn:
fmt.Println("value: ", v, "; ok: ", ok)
if !ok {
fmt.Println("channel closed!!! ")
return // 退出协程
}
default:
if nil == *chn { fmt.Println("channel == nil"); return }
fmt.Println("recv alive print.")
time.Sleep(time.Second * 1)
}
}
}
func main() {
var chn = make(chan int, 0)
//var readOnlyChn = make(<-chan int)
//var writeOnlyChn = make(chan<- int)
/* 先启动接收协程, 在通道写入数据的时候进行读出, 由于之前创建了 0 缓存的通道, 写不同时读出造成堵塞 */
go recv(&chn)
chn<-1024 // 写入1024数据
time.Sleep(time.Second * 2) // 延时两秒, 好让接收线程接收处理写入数据
chn = nil
//close(chn) // 关闭通道
time.Sleep(5 * time.Second)
}
func main() {
var chn = make(chan int, 0) // 创建 0 缓存通道
select {
case chn<-1024: // 写数据
fmt.Println("Writed Done!!!")
case <-time.After(time.Second * 3): // 三秒超时
fmt.Println("Time out")
}
}
func main() {
var chn = make(chan int, 0) // 创建 0 缓存通道
ctx, _ := context.WithTimeout(context.Background(), time.Second * 3)
select {
case chn<-1024: // 写数据
fmt.Println("Writed Done!!!")
case <-ctx.Done(): // 三秒超时
fmt.Println("Done: ", ctx.Err())
}
}
下一篇介绍context