goroutine使用
goroutine 是 go 提供的轻量级并发编程工具,使用起来非常简单,在函数调用之前加上关键字go
即可。例如:
package main
import (
"fmt"
"time"
)
func printMessage(from string) {
fmt.Println("print message from " + from)
}
func main() {
go printMessage("goroutine")
printMessage("main")
time.Sleep(1000 * time.Millisecond)
}
main
方法里面调用了time.Sleep
,这是因为 goroutine 需要调度, 如果 main 方法退出的时候 goroutine 还没有执行完成,就不会打印消息。
在并发编程的场景中,经常需要协同。例如:main
方法等待所有的 goroutine 执行完成。go 里实现协同的方式主要有以下两种:
- 使用管道(channel)
- 使用
sync.WaitGroup
管道(channel)是 go 中非常重要的数据类型,它允许向管道发送消息和从管道接受消息。<-
操作符用来发送或者接受消息,箭头方向表示数据流向。
ch <- v // 向管道发送消息
v := <-ch // 从管道获取消息,同时将值赋给v
管道(channel)的特点是:如果对方没有准备好,发送或者接受将会阻塞。利用这个特性可以实现 main 方法等待 goroutine 执行完成。
package main
import (
"fmt"
"time"
)
func printMessage(from string, done chan bool) {
fmt.Println("print message from " + from)
time.Sleep(time.Second)
done <- true
}
func main() {
channel := make(chan bool)
go printMessage("goroutine", channel)
done := <-channel
fmt.Printf("\ngorountine finish %t\n", done)
fmt.Println("exit main")
}
在main
方法里等待channel
里的消息,而消息会从 goroutine 发送。需要说明的是,上面代码里的channel
变量是 unbuffered channel ,向 unbuffered channel 发送消息时会阻塞直至消息被接受,修改上面的代码即可验证发送消息是否阻塞。
package main
import (
"fmt"
"time"
)
func printMessage(from string, done chan bool) {
fmt.Println("print message from " + from)
done <- true
fmt.Println("send message success " + from)//main方法调用了休眠,故这条打印记录也等待了1s
}
func main() {
channel := make(chan bool)
go printMessage("goroutine", channel)
time.Sleep(time.Second)
done := <-channel
fmt.Printf("\ngorountine finish %t\n", done)
fmt.Println("exit main")
}
与 unbuffered channel 对应的是 buffered channel,在调用make
方法时,指定容量得到的结果便是 buffered channel。buffered channel 的特点是:当管道里未消费的消息超过容量时,发送消息才会会阻塞。
c := make(chan string, 2)
WaitGroup
用来等待一组 goroutine 完成,使用WaitGroup
分为这几步。
- 新建一个
WaitGroup
- 调用
WaitGroup
的Add
方法,参数为需要等待的 goroutine 数。 - 在每一个 goroutine 里调用
WaitGroup
的Done
方法(相当于减一) - 在需要等待其他 goroutine 完成的 goroutine 里调用
WaitGroup
的Wait
方法,这样在其他 goroutine 完成之前,Wait
方法会阻塞。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(3)
for i := 0; i < 3; i++ {
go func() {
defer wg.Done()
fmt.Println("go routine")
}()
}
wg.Wait()
fmt.Printf("\n goroutine finished")
}