nil channel的应用
(转自:https://blog.csdn.net/weixin_33834679/article/details/86026664)
当未为channel分配内存时,channel就是nil channel,例如var ch1 chan int。nil channel会永远阻塞对该channel的读、写操作。
nil channel会阻塞对该channel的所有读、写。所以,可以将某个channel设置为nil,进行强制阻塞,对于select分支来说,就是强制禁用此分支。
以下是一个nil channel的示例:
package main
import (
"fmt"
"math/rand"
"time"
)
// 不断向channel c中发送[0,10)的随机数
func send(c chan int) {
for {
c <- rand.Intn(10)
}
}
func add(c chan int) {
sum := 0
// 1秒后,将向t.C通道发送时间点,使其可读
t := time.NewTimer(1 * time.Second)
for {
// 一秒内,将一直选择第一个case
// 一秒后,t.C可读,将选择第二个case
// c变成nil channel后,两个case分支都将一直阻塞
select {
case input := <-c:
// 不断读取c中的随机数据进行加总
sum = sum + input
case <-t.C:
c = nil
fmt.Println(sum)
}
}
}
func main() {
c := make(chan int)
go add(c)
go send(c)
// 给3秒时间让前两个goroutine有足够时间运行
time.Sleep(3 * time.Second)
}
上面的示例中,send()向通道c不断发送10以内的随机整数,add()则在一秒内不断读取通道c中的数据并进行加总。一秒时间到后,t.C通道就会有数据,第二个case分支就会被选中,第二个case会让第一个case评估的channel变为nil channel,使得第一个case从此永久禁用,因为第二个case没有多余的数据可读,它也被永久禁用。总共3秒之后,main goroutine结束,程序结束。
如果不理解NewTimer(d),换成After(d)是一样的,After(d)和NewTime(d).C是等价的。
func add(c chan int) {
sum := 0
t := time.After(1 * time.Second)
for {
select {
case val := <-c:
sum = sum + val
case <-t:
c = nil
fmt.Println(sum)
}
}
}
资源回收问题
(转自:https://blog.csdn.net/WK_SDU/article/details/91052121)
如何回收废掉的分支这部分资源占用,事实上,因为这部分代码是当前goroutine(执行for select)的不会执行该分支而已,因此不会为该分支分配资源(如内存,cpu等)。不正确的使用,如单纯的从nil channel读取数据就会阻塞住该goroutine,且该goroutine无法被回收(为什么?),从而造成资源泄露,如下图:
func main(){
var ch chan int
data:=<-ch
//or:
for v:=range ch{
//some code in here
}
}
这样的代码会使当前goroutine 永久阻塞 这是不正确的做法。