Go学习笔记—通道补充
1、超时处理
超时对于一个连接外部资源,或者其他一些需要花费执行时间操作的程序是很重要的。
在Go语言中,通过channel
和select
,可以简洁而优雅的处理超时操作。
通过匿名函数实现一个外部调用,在2秒后通过通道c1
返回它的执行结果。
func main(){
c1 := make(chan string,1)
go func(){
//设置通道返回结果的时间
time.Sleep(time.Second * 2)
c1 <- "result 1"
}()
}
通过通道选择器select
,处理超时操作。
func main(){
select {
case res := <-c1:
//不超时直接返回结果
fmt.Println(res)
case <- time.After(time.Second * 1):
//超时返回下面结果
fmt.Println("timeout 1")
}
}
//timeout 1
同样的,设置通道在2秒后返回结果,但是将超时时间设置在3秒后,通道内的值即可正常返回。
func main(){
c2 := make(chan string,1)
go func(){
time.Sleep(time.Second * 2)
c2 <- "result 2"
}()
select {
case res := <-c2:
fmt.Println(res)
case <- time.After(time.Second * 3):
fmt.Println("timeout 2")
}
}
//result 2
2、非阻塞通道操作
常规的使用通道发送和接收数据是阻塞的。
使用带default
子句的select
语句可以实现非阻塞的发送和接收,甚至非阻塞的多路select
。
在例子中实现,如果messages
中存在值,通过select
将这个值带入到<- messages case
中,否则直接到default
子句中执行。
func main(){
messages := make(chan string)
//singals := make(chan bool)
select {
case msg := <-messages:
fmt.Println("received message",msg)
default:
fmt.Println("no message received")
}
}
通过例子可以看出,default
子句可以在通道中未接收任何值时,向用户提供输出。
保证通道在定义之后,未用来传输数据控制台不报错。
func main(){
messages := make(chan string)
msg := "hi"
//go func() {
// messages <- msg
//}()
select {
case messages <- msg:
fmt.Println("sent message",msg)
//case val := <- messages:
// fmt.Println("received message",val)
default :
fmt.Println("no message sent")
}
}
//no message sent
3、通道的关闭
关闭一个通道之后,就不可以再向这个通道传输数据。
关闭通道可以用来传输工作已经完成的信息给通道的接收方。
func main(){
jobs := make(chan int,5)
done := make(chan bool)
//发送数据
for j:=1;j<=3;j++{
jobs <- j
fmt.Println("sent job",j)
}
close(jobs)
fmt.Println("sent all jobs")
//接收数据
go func(){
time.Sleep(time.Second * 1)
for {
j,more := <-jobs
if more{
fmt.Println("received job",j)
}else{
fmt.Println("received all jobs")
done <- true
return
}
}
}()
//通道同步的方法等待任务结束
<-done
}
我们将使用一个jobs
通道来传递main()
中Go协程任务执行的结束信息到一个工作Go协程中。当我们没有多余的任务给这个工作Go协程时,我们将close
这个jobs
通道。
使用j, more := <- jobs
循环的从jobs
接收数据。在接收的这个特殊的二值形式的值中,如果 jobs
已经关闭了,并且通道中所有的值都已经接收完毕,那么more
的值将是false
。当我们完成所有的任务时,将使用这个特性通过done
通道去进行通知。
4、通道的遍历
从通道关闭中,可以看出在通道关闭后,通道中的值还是可以被遍历出来的。
一个非空的通道时可以关闭的,但是在通道中剩下的值依然可以被接收到。
func main(){
queue := make(chan string,2)
queue <- "on"
queue <- "sky"
close(queue)
for elem := range queue{
fmt.Println(elem)
}
}
对通道使用for...range...
进行遍历,只有需要定义一个变量用来存储值即可。