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)
}
}
}