下面的例子很好的反映了select、case、协程的运作,实现的功能是:随机打印1或者2,到达超时后停止。
实现方法:
1个协程设置超时状态,主协程执行随机打印(随机可以用"math/rand"包实现,这里只是为了学习select、case)。
定义1个超时状态chan,标记超时状态。
无缓冲的channel:
1-写入堵塞,直到其他协程读取;
2-读取堵塞,直到其他协程写入。
定义1个1个缓冲chan,使用select、case实现随机读写。
带缓冲的channel:
1-写满后再写堵塞,直到被其他协程读取;
2-为空时去读堵塞,直到被其他协程写入;
3-未写满情况下可一直写入;
4-未空情况下可一直读,读一个少一个,先进先出;
5-只要不触发满后写,空后读,可以实现针对此channel的无堵塞不限读写使用。这个例子就是用了这个处理。
select、case针对channel的I/O进行判断,成功读取或写入就执行对应代码段,都失败就执行default对应代码段
package main
import (
"fmt"
"time"
)
func testSelectBase() {
fmt.Println("select-case语句针对io起作用,写入、读取信道满足其中一个条件就可执行继续")
i := make(chan int, 1) //定义个1个缓冲的channel,这样写入第2个值时才会堵塞,使用无缓冲的话会直接死锁
timeout := make(chan bool)
go func() { //设置一个超时限制下面的循环
time.Sleep(10 * time.Second)
timeout <- true
}()
fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "运行开始")//这个是go显示yyyy-mm-dd HH:MM:SS格式的语法,奇葩!据说是go发布时间...
loop:
for {
select {
case <-timeout: //尝试读取timeout
break loop
default: //因为设置超时的协程需要等超时到后才给timeout写入,所以在此之前一直执行下面代码段
select { //随机执行写入i
case i <- 1: //成功就执行
fmt.Printf("写入了1\t")
case i <- 2: //成功就执行
fmt.Printf("写入了2\t")
}
fmt.Printf("获取i的值是%d\n", <-i) //读取i的值
time.Sleep(500 * time.Millisecond)
}
}
fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "运行完毕")
}
func main() {
testSelectBase()
}
网友评论