介绍
goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是它比线程更小。Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是2KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
-----摘自《Go Web编程》
使用
go 和 channel
为了使goroutine之间可以共享数据,有两种实现方式,一种是使用共享变量,通过加锁和解锁实现。另一种是使用通信的方式。
如果启动了A, B两个goroutine, B要使用A的某个数据d。A将d放入通道channel中, B先进行自己的运算,然后需要d的时候,从这个channel中获取,当获取不到时,B阻塞在这里,一旦A将d放入channel中, B就开始往下执行。
使用通信的方式比加锁方式更加简单,也更符合人的思考习惯。
go关键字启动gouroutine
make函数创建channel
package main
import (
"fmt"
)
func main() {
// make函数创建一个可以存放int值的channel
c := make(chan int)
// go关键字启动一个运行say函数的goroutine
go say("hello world", c)
// 从c这个channel中,取出值,c中没有值时,阻塞当前线程。
// 即阻塞主线程,使得其他goroutine可以运行完。
<- c
}
func say(s string, c chan int) {
for i := 0; i < 5; i++ {
fmt.Println(s)
}
// 将0这个int值放到channel中。
c <- 0
}
range 遍历 channel
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go fabonacci(10, c)
for i := range c {
fmt.Println(i)
}
}
// 求n以内的斐波那契数列
func fabonacci(n int, c chan int) {
x, y := 1, 1
for x < n {
c <- x
x, y = y, x + y
}
// 如果不使用close关闭channel,那么range便利channel的时候会出错
close(c)
}
select
select可以监控多个channel,默认是阻塞状态,当某个channel接收或发送数据时,case的语句开始执行。 当多个channel同时接收或发送数据时,select执行顺序是随机的。
package main
import (
"fmt"
)
func main() {
c := make(chan int) // 存放斐波那契额数列
quit := make(chan int) // 控制函数退出
go func() {
// 不断从c中取10次数据
for i := 0; i < 10; i++ {
fmt.Println(<- c)
}
// 控制fabonacci函数退出
quit <- 1
}()
fabonacci(c, quit)
fmt.Println("main exit")
}
func fabonacci (c, quit chan int) {
x, y := 1, 1
for {
select {
case c <- x: // 不断向c中塞入数据
x, y = y, x+y
case <- quit: // quit中被塞入数据后,函数退出
fmt.Println("fabonacci exit")
return
}
}
}
select 实现超时
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int)
go func() {
for{
select {
case <- c:
fmt.Println("haha")
case <- time.After(time.Second * 3): // 等待3s后会触发
fmt.Println("超时了")
return
}
}
}()
// 为了防止main退出
o := make(chan int)
<- o
}