一、goroutine简介
goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心。goroutine使用方式非常的简单,只需使用go关键字即可启动一个协程,并且它是处于异步方式运行,你不需要等它运行完成以后在执行以后的代码。
二、goroutine内部原理
概念介绍
在进行实现原理之前,了解下一些关键性术语的概念。
并发
一个cpu上能同时执行多项任务,在很短时间内,cpu来回切换任务执行(在某段很短时间内执行程序a,然后又迅速得切换到程序b去执行),有时间上的重叠(宏观上是同时的,微观仍是顺序执行),这样看起来多个任务像是同时执行,这就是并发。
并行
当系统有多个cpu时,每个cpu同一时刻都运行任务,互不抢占自己所在的cpu资源,同时进行,称为并行。
进程
cpu在切换程序的时候,如果不保存上一个程序的状态(也就是我们常说的context--上下文),直接切换下一个程序,就会丢失上一个程序的一系列状态,于是引入了进程这个概念,用以划分好程序运行时所需要的资源。因此进程就是一个程序运行时候的所需要的基本资源单位(也可以说是程序运行的一个实体)。
线程
cpu切换多个进程的时候,会花费不少的时间,因为切换进程需要切换到内核态,而每次调度需要内核态都需要读取用户态的数据,进程一旦多起来,cpu调度会消耗一大堆资源,因此引入了线程的概念,线程本身几乎不占有资源,他们共享进程里的资源,内核调度起来不会那么像进程切换那么耗费资源。
协程
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作执行者则是用户自身程序,goroutine也是协程。
调度模型简介
groutine能拥有强大的并发实现是通过GPM调度模型实现,下面就来解释下goroutine的调度模型。
调度实现
当一个OS线程M0陷入阻塞时(如下图),P转而在运行M1,图中的M1可能是正被创建,或者从线程缓存中取出。
三、使用goroutine
基本使用
设置goroutine运行的cpu数量,最新版本的go已经默认已经设置了。
使用示例
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"<span style="color: #000000;">
)
func cal(a <span style="color: #0000ff;">int,b <span style="color: #0000ff;">int<span style="color: #000000;"> ) {
c := a+<span style="color: #000000;">b
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%d + %d = %d\n<span style="color: #800000;">"<span style="color: #000000;">,a,b,c)
}
func main() {
<span style="color: #0000ff;">for i :=<span style="color: #800080;">0 ; i<<span style="color: #800080;">10 ;i++<span style="color: #000000;">{
go cal(i,i+<span style="color: #800080;">1) <span style="color: #008000;">//<span style="color: #008000;">启动10个goroutine 来计算
<span style="color: #000000;"> }
time.Sleep(time.Second * <span style="color: #800080;">2) <span style="color: #008000;">//<span style="color: #008000;"> sleep作用是为了等待所有任务完成
<span style="color: #000000;">}
<span style="color: #008000;">//<span style="color: #008000;">结果
<span style="color: #008000;">//<span style="color: #008000;">8 + 9 = 17
<span style="color: #008000;">//<span style="color: #008000;">9 + 10 = 19
<span style="color: #008000;">//<span style="color: #008000;">4 + 5 = 9
<span style="color: #008000;">//<span style="color: #008000;">5 + 6 = 11
<span style="color: #008000;">//<span style="color: #008000;">0 + 1 = 1
<span style="color: #008000;">//<span style="color: #008000;">1 + 2 = 3
<span style="color: #008000;">//<span style="color: #008000;">2 + 3 = 5
<span style="color: #008000;">//<span style="color: #008000;">3 + 4 = 7
<span style="color: #008000;">//<span style="color: #008000;">7 + 8 = 15
<span style="color: #008000;">//<span style="color: #008000;">6 + 7 = 13
goroutine异常捕捉
当启动多个goroutine时,如果其中一个goroutine异常了,并且我们并没有对进行异常处理,那么整个程序都会终止,所以我们在编写程序时候最好每个goroutine所运行的函数都做异常处理,异常处理采用recover
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"<span style="color: #000000;">
)
func addele(a []<span style="color: #0000ff;">int,i <span style="color: #0000ff;">int<span style="color: #000000;">) {
defer func() { <span style="color: #008000;">//<span style="color: #008000;">匿名函数捕获错误
err :=<span style="color: #000000;"> recover()
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">add ele fail<span style="color: #800000;">"<span style="color: #000000;">)
}
}()
a[i]=<span style="color: #000000;">i
fmt.Println(a)
}
func main() {
Arry := make([]<span style="color: #0000ff;">int,<span style="color: #800080;">4<span style="color: #000000;">)
<span style="color: #0000ff;">for i :=<span style="color: #800080;">0 ; i<<span style="color: #800080;">10 ;i++<span style="color: #000000;">{
go addele(Arry,i)
}
time.Sleep(time.Second * <span style="color: #800080;">2<span style="color: #000000;">)
}
<span style="color: #008000;">//<span style="color: #008000;">结果
<span style="color: #000000;">add ele fail
[<span style="color: #800080;">0 <span style="color: #800080;">0 <span style="color: #800080;">0 <span style="color: #800080;">0<span style="color: #000000;">]
[<span style="color: #800080;">0 <span style="color: #800080;">1 <span style="color: #800080;">0 <span style="color: #800080;">0<span style="color: #000000;">]
[<span style="color: #800080;">0 <span style="color: #800080;">1 <span style="color: #800080;">2 <span style="color: #800080;">0<span style="color: #000000;">]
[<span style="color: #800080;">0 <span style="color: #800080;">1 <span style="color: #800080;">2 <span style="color: #800080;">3<span style="color: #000000;">]
add ele fail
add ele fail
add ele fail
add ele fail
add ele fail
同步的goroutine
由于goroutine是异步执行的,那很有可能出现主程序退出时还有goroutine没有执行完,此时goroutine也会跟着退出。此时如果想等到所有goroutine任务执行完毕才退出,go提供了sync包和channel来解决同步问题,当然如果你能预测每个goroutine执行的时间,你还可以通过time.Sleep方式等待所有的groutine执行完成以后在退出程序(如上面的列子)。
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">sync<span style="color: #800000;">"<span style="color: #000000;">
)
func cal(a <span style="color: #0000ff;">int,b <span style="color: #0000ff;">int,n *<span style="color: #000000;">sync.WaitGroup) {
c := a+<span style="color: #000000;">b
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%d + %d = %d\n<span style="color: #800000;">"<span style="color: #000000;">,c)
defer n.Done() <span style="color: #008000;">//<span style="color: #008000;">goroutinue完成后,WaitGroup的计数-1
<span style="color: #000000;">
}
func main() {
<span style="color: #0000ff;">var go_sync sync.WaitGroup <span style="color: #008000;">//<span style="color: #008000;">声明一个WaitGroup变量
<span style="color: #0000ff;">for i :=<span style="color: #800080;">0 ; i<<span style="color: #800080;">10 ;i++<span style="color: #000000;">{
go_sync.Add(<span style="color: #800080;">1) <span style="color: #008000;">//<span style="color: #008000;"> WaitGroup的计数加1
go cal(i,i+<span style="color: #800080;">1,&<span style="color: #000000;">go_sync)
}
go_sync.Wait() <span style="color: #008000;">//<span style="color: #008000;">等待所有goroutine执行完毕
<span style="color: #000000;">}
<span style="color: #008000;">//<span style="color: #008000;">结果
<span style="color: #800080;">9 + <span style="color: #800080;">10 = <span style="color: #800080;">19
<span style="color: #800080;">2 + <span style="color: #800080;">3 = <span style="color: #800080;">5
<span style="color: #800080;">3 + <span style="color: #800080;">4 = <span style="color: #800080;">7
<span style="color: #800080;">4 + <span style="color: #800080;">5 = <span style="color: #800080;">9
<span style="color: #800080;">5 + <span style="color: #800080;">6 = <span style="color: #800080;">11
<span style="color: #800080;">1 + <span style="color: #800080;">2 = <span style="color: #800080;">3
<span style="color: #800080;">6 + <span style="color: #800080;">7 = <span style="color: #800080;">13
<span style="color: #800080;">7 + <span style="color: #800080;">8 = <span style="color: #800080;">15
<span style="color: #800080;">0 + <span style="color: #800080;">1 = <span style="color: #800080;">1
<span style="color: #800080;">8 + <span style="color: #800080;">9 = <span style="color: #800080;">17
示例二:通过channel实现goroutine之间的同步。
实现方式:通过channel能在多个groutine之间通讯,当一个goroutine完成时候向channel发送退出信号,等所有goroutine退出时候,利用for循环channe去channel中的信号,若取不到数据会阻塞原理,等待所有goroutine执行完毕,使用该方法有个前提是你已经知道了你启动了多少个goroutine。
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"<span style="color: #000000;">
)
func cal(a <span style="color: #0000ff;">int,Exitchan chan <span style="color: #0000ff;">bool<span style="color: #000000;">) {
c := a+<span style="color: #000000;">b
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">%d + %d = %d\n<span style="color: #800000;">"<span style="color: #000000;">,c)
time.Sleep(time.Second*<span style="color: #800080;">2<span style="color: #000000;">)
Exitchan <- <span style="color: #0000ff;">true<span style="color: #000000;">
}
func main() {
Exitchan :</span>= make(chan <span style="color: #0000ff;">bool</span>,<span style="color: #800080;">10</span>) <span style="color: #008000;">//</span><span style="color: #008000;">声明并分配管道内存</span>
<span style="color: #0000ff;">for</span> i :=<span style="color: #800080;">0</span> ; i<<span style="color: #800080;">10</span> ;i++<span style="color: #000000;">{
go cal(i,i</span>+<span style="color: #800080;">1</span><span style="color: #000000;">,Exitchan)
}
</span><span style="color: #0000ff;">for</span> j :=<span style="color: #800080;">0</span>; j<<span style="color: #800080;">10</span>; j++<span style="color: #000000;">{
</span><- Exitchan <span style="color: #008000;">//</span><span style="color: #008000;">取信号数据,如果取不到则会阻塞</span>
<span style="color: #000000;"> }
close(Exitchan) <span style="color: #008000;">//<span style="color: #008000;"> 关闭管道
}
goroutine之间的通讯
goroutine本质上是协程,可以理解为不受内核调度,而受go调度器管理的线程。goroutine之间可以通过channel进行通信或者说是数据共享,当然你也可以使用全局变量来进行数据共享。
示例:使用channel模拟消费者和生产者模式
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">sync<span style="color: #800000;">"<span style="color: #000000;">
)
func Productor(mychan chan <span style="color: #0000ff;">int,data <span style="color: #0000ff;">int,wait <span style="color: #000000;">sync.WaitGroup) {
mychan <-<span style="color: #000000;"> data
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">product data:<span style="color: #800000;">"<span style="color: #000000;">,data)
wait.Done()
}
func Consumer(mychan chan <span style="color: #0000ff;">int,wait <span style="color: #000000;">sync.WaitGroup) {
a := <-<span style="color: #000000;"> mychan
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">consumer data:<span style="color: #800000;">"<span style="color: #000000;">,a)
wait.Done()
}
func main() {
datachan :</span>= make(chan <span style="color: #0000ff;">int</span>,<span style="color: #800080;">100</span>) <span style="color: #008000;">//</span><span style="color: #008000;">通讯数据管道</span>
<span style="color: #0000ff;">var</span><span style="color: #000000;"> wg sync.WaitGroup
</span><span style="color: #0000ff;">for</span> i := <span style="color: #800080;">0</span>; i < <span style="color: #800080;">10</span>; i++<span style="color: #000000;"> {
go Productor(datachan,i,</span>&wg) <span style="color: #008000;">//</span><span style="color: #008000;">生产数据</span>
wg.Add(<span style="color: #800080;">1</span><span style="color: #000000;">)
}
</span><span style="color: #0000ff;">for</span> j := <span style="color: #800080;">0</span>; j < <span style="color: #800080;">10</span>; j++<span style="color: #000000;"> {
go Consumer(datachan,</span>&wg) <span style="color: #008000;">//</span><span style="color: #008000;">消费数据</span>
wg.Add(<span style="color: #800080;">1</span><span style="color: #000000;">)
}
wg.Wait()
}
<span style="color: #008000;">//<span style="color: #008000;">结果
consumer data: <span style="color: #800080;">4<span style="color: #000000;">
product data: <span style="color: #800080;">5<span style="color: #000000;">
product data: <span style="color: #800080;">6<span style="color: #000000;">
product data: <span style="color: #800080;">7<span style="color: #000000;">
product data: <span style="color: #800080;">8<span style="color: #000000;">
product data: <span style="color: #800080;">9<span style="color: #000000;">
consumer data: <span style="color: #800080;">1<span style="color: #000000;">
consumer data: <span style="color: #800080;">5<span style="color: #000000;">
consumer data: <span style="color: #800080;">6<span style="color: #000000;">
consumer data: <span style="color: #800080;">7<span style="color: #000000;">
consumer data: <span style="color: #800080;">8<span style="color: #000000;">
consumer data: <span style="color: #800080;">9<span style="color: #000000;">
product data: <span style="color: #800080;">2<span style="color: #000000;">
consumer data: <span style="color: #800080;">2<span style="color: #000000;">
product data: <span style="color: #800080;">3<span style="color: #000000;">
consumer data: <span style="color: #800080;">3<span style="color: #000000;">
product data: <span style="color: #800080;">4<span style="color: #000000;">
consumer data: <span style="color: #800080;">0<span style="color: #000000;">
product data: <span style="color: #800080;">0<span style="color: #000000;">
product data: <span style="color: #800080;">1
四、channel
简介
channel俗称管道,用于数据传递或数据共享,其本质是一个先进先出的队列,使用goroutine+channel进行数据通讯简单高效,同时也线程安全,多个goroutine可同时修改一个channel,不需要加锁。
channel可分为三种类型:
只读channel:只能读channel里面数据,不可写入
只写channel:只能写数据,不可读
一般channel:可读可写
channel使用
定义和声明
read_only := make (<-chan <span style="color: #0000ff;">int,<span style="color: #800080;">10)<span style="color: #008000;">//<span style="color: #008000;">定义只读的channel
write_only := make (chan<- <span style="color: #0000ff;">int,<span style="color: #800080;">10)<span style="color: #008000;">//<span style="color: #008000;">定义只写的channel
read_write := make (chan <span style="color: #0000ff;">int,<span style="color: #800080;">10)<span style="color: #008000;">//<span style="color: #008000;">可同时读写
读写数据
需要注意的是:
@H_47_502@循环管道
需要注意的是:
@H_47_502@<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"<span style="color: #000000;">
)
func main() {
mychannel := make(chan <span style="color: #0000ff;">int,<span style="color: #800080;">10<span style="color: #000000;">)
<span style="color: #0000ff;">for i := <span style="color: #800080;">0;i < <span style="color: #800080;">10;i++<span style="color: #000000;">{
mychannel <-<span style="color: #000000;"> i
}<span style="color: #000000;">
close(mychannel) <span style="color: #008000;">//<span style="color: #008000;">关闭管道
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">data lenght: <span style="color: #800000;">"<span style="color: #000000;">,len(mychannel))
<span style="color: #0000ff;">for v := range mychannel { <span style="color: #008000;">//<span style="color: #008000;">循环管道
<span style="color: #000000;"> fmt.Println(v)
}
fmt.Printf(<span style="color: #800000;">"<span style="color: #800000;">data lenght: %d<span style="color: #800000;">"<span style="color: #000000;">,len(mychannel))
}
带缓冲区channe和不带缓冲区channel
带缓冲区channel:定义声明时候制定了缓冲区大小(长度),可以保存多个数据。
不带缓冲区channel:只能存一个数据,并且只有当该数据被取出时候才能存下一个数据。
不带缓冲区示例:
func test(c chan <span style="color: #0000ff;">int<span style="color: #000000;">) {
<span style="color: #0000ff;">for i := <span style="color: #800080;">0; i < <span style="color: #800080;">10; i++<span style="color: #000000;"> {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">send <span style="color: #800000;">"<span style="color: #000000;">,i)
c <-<span style="color: #000000;"> i
}
}
func main() {
ch := make(chan <span style="color: #0000ff;">int<span style="color: #000000;">)
go test(ch)
<span style="color: #0000ff;">for j := <span style="color: #800080;">0; j < <span style="color: #800080;">10; j++<span style="color: #000000;"> {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">get <span style="color: #800000;">",<-<span style="color: #000000;">ch)
}
}
<span style="color: #008000;">//<span style="color: #008000;">结果:
send <span style="color: #800080;">0<span style="color: #000000;">
send <span style="color: #800080;">1
<span style="color: #0000ff;">get <span style="color: #800080;">0
<span style="color: #0000ff;">get <span style="color: #800080;">1<span style="color: #000000;">
send <span style="color: #800080;">2<span style="color: #000000;">
send <span style="color: #800080;">3
<span style="color: #0000ff;">get <span style="color: #800080;">2
<span style="color: #0000ff;">get <span style="color: #800080;">3<span style="color: #000000;">
send <span style="color: #800080;">4<span style="color: #000000;">
send <span style="color: #800080;">5
<span style="color: #0000ff;">get <span style="color: #800080;">4
<span style="color: #0000ff;">get <span style="color: #800080;">5<span style="color: #000000;">
send <span style="color: #800080;">6<span style="color: #000000;">
send <span style="color: #800080;">7
<span style="color: #0000ff;">get <span style="color: #800080;">6
<span style="color: #0000ff;">get <span style="color: #800080;">7<span style="color: #000000;">
send <span style="color: #800080;">8<span style="color: #000000;">
send <span style="color: #800080;">9
<span style="color: #0000ff;">get <span style="color: #800080;">8
<span style="color: #0000ff;">get <span style="color: #800080;">9
channel实现作业池
我们创建三个channel,一个channel用于接受任务,一个channel用于保持结果,还有个channel用于决定程序退出的时候。
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"<span style="color: #000000;">
)
func Task(taskch,resch chan <span style="color: #0000ff;">int,exitch chan <span style="color: #0000ff;">bool<span style="color: #000000;">) {
defer func() { <span style="color: #008000;">//<span style="color: #008000;">异常处理
err :=<span style="color: #000000;"> recover()
<span style="color: #0000ff;">if err !=<span style="color: #000000;"> nil {
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">do task error:<span style="color: #800000;">"<span style="color: #000000;">,err)
<span style="color: #0000ff;">return<span style="color: #000000;">
}
}()
</span><span style="color: #0000ff;">for</span> t := range taskch { <span style="color: #008000;">//</span><span style="color: #008000;"> 处理任务</span>
fmt.Println(<span style="color: #800000;">"</span><span style="color: #800000;">do task :</span><span style="color: #800000;">"</span><span style="color: #000000;">,t)
resch </span><- t <span style="color: #008000;">//
<span style="color: #000000;"> }
exitch <- <span style="color: #0000ff;">true <span style="color: #008000;">//<span style="color: #008000;">处理完发送退出信号
<span style="color: #000000;">}
func main() {
taskch := make(chan <span style="color: #0000ff;">int,<span style="color: #800080;">20) <span style="color: #008000;">//<span style="color: #008000;">任务管道
resch := make(chan <span style="color: #0000ff;">int,<span style="color: #800080;">20) <span style="color: #008000;">//<span style="color: #008000;">结果管道
exitch := make(chan <span style="color: #0000ff;">bool,<span style="color: #800080;">5) <span style="color: #008000;">//<span style="color: #008000;">退出管道
<span style="color: #000000;"> go func() {
<span style="color: #0000ff;">for i := <span style="color: #800080;">0; i < <span style="color: #800080;">10; i++<span style="color: #000000;"> {
taskch <-<span style="color: #000000;"> i
}
close(taskch)
}()
</span><span style="color: #0000ff;">for</span> i := <span style="color: #800080;">0</span>; i < <span style="color: #800080;">5</span>; i++ { <span style="color: #008000;">//</span><span style="color: #008000;">启动5个goroutine做任务</span>
<span style="color: #000000;"> go Task(taskch,resch,exitch)
}
go func() { </span><span style="color: #008000;">//</span><span style="color: #008000;">等5个goroutine结束</span>
<span style="color: #0000ff;">for</span> i := <span style="color: #800080;">0</span>; i < <span style="color: #800080;">5</span>; i++<span style="color: #000000;"> {
</span><-<span style="color: #000000;">exitch
}
close(resch) </span><span style="color: #008000;">//</span><span style="color: #008000;">任务处理完成<a href="/tag/guanbi/" target="_blank" class="keywords">关闭</a>结果管道,不然range报错</span>
close(exitch) <span style="color: #008000;">//</span><span style="color: #008000;"><a href="/tag/guanbi/" target="_blank" class="keywords">关闭</a><a href="/tag/tuichu/" target="_blank" class="keywords">退出</a>管道</span>
<span style="color: #000000;"> }()
</span><span style="color: #0000ff;">for</span> res := range resch{ <span style="color: #008000;">//</span><span style="color: #008000;">打印结果</span>
fmt.Println(<span style="color: #800000;">"</span><span style="color: #800000;">task res:</span><span style="color: #800000;">"</span><span style="color: #000000;">,res)
}
}
只读channel和只写channel
一般定义只读和只写的管道意义不大,更多时候我们可以在参数传递时候指明管道可读还是可写,即使当前管道是可读写的。
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"<span style="color: #000000;">
)
<span style="color: #008000;">//<span style="color: #008000;">只能向chan里写数据
func send(c chan<- <span style="color: #0000ff;">int<span style="color: #000000;">) {
<span style="color: #0000ff;">for i := <span style="color: #800080;">0; i < <span style="color: #800080;">10; i++<span style="color: #000000;"> {
c <-<span style="color: #000000;"> i
}
}
<span style="color: #008000;">//<span style="color: #008000;">只能取channel中的数据
func <span style="color: #0000ff;">get(c <-chan <span style="color: #0000ff;">int<span style="color: #000000;">) {
<span style="color: #0000ff;">for i :=<span style="color: #000000;"> range c {
fmt.Println(i)
}
}
func main() {
c := make(chan <span style="color: #0000ff;">int<span style="color: #000000;">)
go send(c)
go <span style="color: #0000ff;">get<span style="color: #000000;">(c)
time.Sleep(time.Second*<span style="color: #800080;">1<span style="color: #000000;">)
}
<span style="color: #008000;">//<span style="color: #008000;">结果
<span style="color: #800080;">0
<span style="color: #800080;">1
<span style="color: #800080;">2
<span style="color: #800080;">3
<span style="color: #800080;">4
<span style="color: #800080;">5
<span style="color: #800080;">6
<span style="color: #800080;">7
<span style="color: #800080;">8
<span style="color: #800080;">9
select-case实现非阻塞channel
原理通过select+case加入一组管道,当满足(这里说的满足意思是有数据可读或者可写)select中的某个case时候,那么该case返回,若都不满足case,则走default分支。
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"<span style="color: #000000;">
)
func send(c chan <span style="color: #0000ff;">int<span style="color: #000000;">) {
<span style="color: #0000ff;">for i :=<span style="color: #800080;">1 ; i<<span style="color: #800080;">10 ;i++<span style="color: #000000;"> {
c <-<span style="color: #000000;">i
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">send data : <span style="color: #800000;">"<span style="color: #000000;">,i)
}
}
func main() {
resch := make(chan <span style="color: #0000ff;">int,<span style="color: #800080;">20<span style="color: #000000;">)
strch := make(chan <span style="color: #0000ff;">string,<span style="color: #800080;">10<span style="color: #000000;">)
go send(resch)
strch <- <span style="color: #800000;">"<span style="color: #800000;">wd<span style="color: #800000;">"
<span style="color: #0000ff;">select<span style="color: #000000;"> {
<span style="color: #0000ff;">case a := <-<span style="color: #000000;">resch:
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">get data : <span style="color: #800000;">"<span style="color: #000000;">,a)
<span style="color: #0000ff;">case b := <-<span style="color: #000000;">strch:
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">get data : <span style="color: #800000;">"<span style="color: #000000;">,b)
<span style="color: #0000ff;">default<span style="color: #000000;">:
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">no channel actvie<span style="color: #800000;">"<span style="color: #000000;">)
}
}
<span style="color: #008000;">//<span style="color: #008000;">结果:get data : wd
channel频率控制
在对channel进行读写的时,go还提供了非常人性化的操作,那就是对读写的频率控制,通过time.Ticke实现
示例:
<span style="color: #800000;">"<span style="color: #800000;">time<span style="color: #800000;">"
<span style="color: #800000;">"<span style="color: #800000;">fmt<span style="color: #800000;">"<span style="color: #000000;">
)
func main(){
requests:= make(chan <span style="color: #0000ff;">int,<span style="color: #800080;">5<span style="color: #000000;">)
<span style="color: #0000ff;">for i:=<span style="color: #800080;">1;i<<span style="color: #800080;">5;i++<span style="color: #000000;">{
requests<-<span style="color: #000000;">i
}
close(requests)
limiter := time.Tick(time.Second*<span style="color: #800080;">1<span style="color: #000000;">)
<span style="color: #0000ff;">for req:=<span style="color: #000000;">range requests{
<-<span style="color: #000000;">limiter
fmt.Println(<span style="color: #800000;">"<span style="color: #800000;">requets<span style="color: #800000;">",req,time.Now()) <span style="color: #008000;">//<span style="color: #008000;">执行到这里,需要隔1秒才继续往下执行,time.Tick(timer)上面已定义
<span style="color: #000000;"> }
}
<span style="color: #008000;">//<span style="color: #008000;">结果:
requets <span style="color: #800080;">1 <span style="color: #800080;">2018-<span style="color: #800080;">07-<span style="color: #800080;">06 <span style="color: #800080;">10:<span style="color: #800080;">17:<span style="color: #800080;">35.98056403 +<span style="color: #800080;">0800 CST m=+<span style="color: #800080;">1.004248763<span style="color: #000000;">
requets <span style="color: #800080;">2 <span style="color: #800080;">2018-<span style="color: #800080;">07-<span style="color: #800080;">06 <span style="color: #800080;">10:<span style="color: #800080;">17:<span style="color: #800080;">36.978123472 +<span style="color: #800080;">0800 CST m=+<span style="color: #800080;">2.001798205<span style="color: #000000;">
requets <span style="color: #800080;">3 <span style="color: #800080;">2018-<span style="color: #800080;">07-<span style="color: #800080;">06 <span style="color: #800080;">10:<span style="color: #800080;">17:<span style="color: #800080;">37.980869517 +<span style="color: #800080;">0800 CST m=+<span style="color: #800080;">3.004544250<span style="color: #000000;">
requets <span style="color: #800080;">4 <span style="color: #800080;">2018-<span style="color: #800080;">07-<span style="color: #800080;">06 <span style="color: #800080;">10:<span style="color: #800080;">17:<span style="color: #800080;">38.976868836 +<span style="color: #800080;">0800 CST m=+<span style="color: #800080;">4.000533569