Golang极简入门教程(三):并发支持
Golang运行时(runtime)管理了一种轻量级线程,被叫做goroutine。创建数十万级的goroutine是没有问题的。范例:
packagemain import( "fmt" "time" ) funcsay(sstring){ fori:=0;i<5;i++{ time.Sleep(100*time.Millisecond) fmt.Println(s) } } funcmain(){ //开启一个goroutine执行say函数 gosay("world") say("hello") }
我们使用channel和goroutine通讯。channel中是一种带有类型的通道,被用于接收和发送特定类型的值。操作符<-被叫做channel操作符(这个操作符中箭头表明了值的流向):
//发送v到channelch ch<-v //接收channelch中的值并赋值给v v:=<-ch
使用channel和goroutine通讯能够避免显式使用锁机制,通过channel发送和接收值时默认是阻塞的。
通过make函数创建channel:
//int指定channel收发值的类型为int ch:=make(chanint)
一个完整的例子:
packagemain import"fmt" //计算数组a中所有元素值之和 funcsum(a[]int,cchanint){ sum:=0 for_,v:=rangea{ sum+=v } //计算结果发送到channelc c<-sum } funcmain(){ a:=[]int{7,2,8,-9,4,0} //创建channelc c:=make(chanint) gosum(a[:len(a)/2],c) gosum(a[len(a)/2:],c) //接收两个goroutine发送的计算结果 x,y:=<-c,<-c fmt.Println(x,y,x+y) }packagemain import"fmt" //计算数组a中所有元素值之和 funcsum(a[]int,cchanint){ sum:=0 for_,v:=rangea{ sum+=v } //计算结果发送到channelc c<-sum } funcmain(){ a:=[]int{7,2,8,-9,4,0} //创建channelc c:=make(chanint) gosum(a[:len(a)/2],c) gosum(a[len(a)/2:],c) //接收两个goroutine发送的计算结果 x,y:=<-c,<-c fmt.Println(x,y,x+y) }
channel可以带有一个缓冲区(buffer)来缓存被传递的值,向channel中发送时只有缓冲区满的情况下会阻塞,接收channel中的值时只有在缓冲区空的情况下阻塞:
packagemain import"fmt" funcmain(){ //创建channel,缓冲区长度为2 c:=make(chanint,2) //由于channel的缓冲区长度为2 //因此发送不会阻塞 c<-1 c<-2 fmt.Println(<-c) fmt.Println(<-c) }
发送者可以调用close来关闭channel,接收者可以检测到channel是否被关闭:
//这里的ok为false表示已经没有值可以接收了,并且channel被关闭了 v,ok:=<-ch
不要向已经关闭的channel发送值了(willcauseapanic)。
我们可以使用forrange来接收channel中的值:
packagemain import"fmt" funcfibonacci(nint,cchanint){ x,y:=0,1 fori:=0;i<n;i++{ c<-x x,y=y,x+y } //必须要关闭c close(c) } funcmain(){ c:=make(chanint,10) gofibonacci(cap(c),c) //这里for和range组合使用 //不断的接收c中的值一直到它被关闭 fori:=rangec{ fmt.Println(i) } }
通常来说,我们不需要主动的关闭channel。但有时候接收者必须被告知已经没有值可以接收了,这时候主动关闭是必要的,例如终止forrange循环。
使用select语句可以让一个goroutine等待多个通讯操作。select会阻塞直到某个case能够运行,如果同时存在多个可执行的,那么将随机选择一个:
packagemain import"fmt" funcfibonacci(c,quitchanint){ x,y:=0,1 for{ select{ casec<-x: x,y=y,x+y //控制此线程退出 case<-quit: fmt.Println("quit") return } } } funcmain(){ c:=make(chanint) quit:=make(chanint) gofunc(){ fori:=0;i<10;i++{ fmt.Println(<-c) } quit<-0 }() fibonacci(c,quit) }
select中的default会在没有任何case可执行时执行(类似于switch):
packagemain import( "fmt" "time" ) funcmain(){ //创建一个tickchannel //在100毫秒后会向tickchannel中发送当前时间 tick:=time.Tick(100*time.Millisecond) //创建一个boomchannel //在500毫秒后会向boomchannel中发送当前时间 boom:=time.After(500*time.Millisecond) for{ select{ case<-tick: fmt.Println("tick.") case<-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50*time.Millisecond) } } }