golang基础-chain的使用rangeselect
Posted 进击的小猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang基础-chain的使用rangeselect相关的知识,希望对你有一定的参考价值。
文章目录
chan基础使用
我们直接来看代码
package main
import (
"fmt"
"time"
)
var message = make(chan string)
func go1()
message <- "hello1"
message <- "hello2"
message <- "hello3"
//message <- "hello4"
func go2()
time.Sleep(2*time.Second)
str:= <-message
str = str + "go"
message <- str
func main()
go go1()
go go2()
time.Sleep(3*time.Second)
fmt.Println(<-message)
fmt.Println(<-message)
fmt.Println(<-message)
输出结果是
hello1go
hello2
hello3
或者
hello2
hello1go
hello3
或者
hello2
hello3
hello1go
以上代码意思就是:
定义了一个容量为1的chan,在go1里面依次存储3个字符串,但是容量是1,首先会把hello1存入,go1容量占满,挂起阻塞,然后发信号,进行chan通讯,然后2秒后,go2从管道里面取出来,str:= <-message,到这个节点取出完毕,go2会通知go1继续存储,由于管道间通讯需要耗时操作,虽然时间很小,由于cpu性能或者其他的原因,可能g2在发出信号时候,go1还没有进行存储操作,go2里面会把拼接的字符存入,还有可能是go1存储了,然后拼接字符串没有存储,所以在main中,依次取出来会出现3种情况
我们将上述代码改下
var message = make(chan string,3)
由于容量扩充为3,所以go1会通过fifo方式,依次存入hello1,hello2,hello3,可以理解成队列的先进先出,然后2秒后,go2会取出hello1,取出完毕,管道里面的数据形态是【hello2,hello3,空位】,然后讲取出来的hello1,通过拼接字符串的形式,存入到管道的末尾,【hello2,hello3,hello1go】,然后3秒后,主函数里面就开始依次取出即可,结果如下:
hello2
hello3
hello1go
我们在例2的基础上,再次进行修改,注意是在例2的基础上进行修改,在容量为3的chan里面,在go1里面存入4个字符串,
func go1()
message <- "hello1"
message <- "hello2"
message <- "hello3"
message <- "hello4"
结果如下:跟例1的原理一样,产生如下的结果
hello2
hello3
hello4
或者
hello2
hello3
hello1go
range获取
以上的demo有几个需要优化的地方
1、我们可以将chan通过参数,传递到go协程里面
2、当我们从chan里面取出数据时,我们可以使用range,而不是一个个的去取
3、chan在为空时候,range依然会去遍历读取数据,我们需要关闭chan
优化后的代码如下:
package main
import (
"fmt"
"time"
)
func go1(message chan string)
message <- "hello1"
message <- "hello2"
message <- "hello3"
message <- "hello4"
func go2(message chan string)
time.Sleep(2*time.Second)
str:= <-message
str = str + "go"
message <- str
//关闭chan
close(message)
func main()
var message = make(chan string,3)
go go1(message)
go go2(message)
time.Sleep(3*time.Second)
//range读取
for val:=range message
fmt.Println(val)
如果不关闭chan的话,会报如下的死锁错误信息:
就是协程里面没有存入,主线程里面也无法读取数据,主线程在等协程存入数据,就是互相等待的状态
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
E:/golang/gland_pro/flow-statistics/src/test/test.go:29 +0x124
select多队列
package main
import (
"fmt"
"strconv"
"time"
)
func go1(ch chan string)
for i:=0;i<5;i++
ch <- "hello"+ strconv.Itoa(i)
//time.Sleep(1*time.Second)
func go2(ch chan int)
for i:=0;i<5;i++
ch <- i
//time.Sleep(2*time.Second)
func main()
chan1:=make(chan string,3)
chan2:=make(chan int,5)
go go1(chan1)
go go2(chan2)
time.Sleep(1*time.Second)
Loop:
for
select
case str,ok:=<-chan1:
if !ok
fmt.Println("ch1 failed")
continue
fmt.Println(str)
case p,oke := <-chan2:
if !oke
fmt.Println("ch2 failed")
continue
fmt.Println(p)
default:
fmt.Println("ds")
break Loop
结果如下:
0
hello0
1
hello1
hello2
hello3
2
hello4
3
4
ds
Process finished with exit code 0
简单分析下,以上只是一个demo,这个demo有很多不完善的地方
定义了2个协程,然后存入数据,通过select去循环获取多个协程里面的数据,在这里我通过time.Sleep(1*time.Second) 进行主线程阻塞,为了测试效果,一秒钟过后,就循环去读取各个协程里面的数据,如果某个协程里面没有数据了,就终止本地循环,去进行下一次读取,当2个chan都读取完毕了,就进入defalut,就终止循环退出主线程即可
缺陷:1、这里没有用到close(chan)操作,理想的情况下,应该读取完毕某个管道,就应该关闭某个管道,如果网友知道方案,可以留言哦
2、主线程通过阻塞方式,这样就避免了,直接执行for循环的default操作,为了demo效果出此对策
以上是关于golang基础-chain的使用rangeselect的主要内容,如果未能解决你的问题,请参考以下文章
Linux内核基础--事件通知链(notifier chain)转
Dweb3.0的核心基础设施?NA(Nirvana)Chain加速开凿链上域名流量通道