Go微服务实战
上QQ阅读APP看书,第一时间看更新

5.1.2 goroutine的基本使用

goroutine的使用比较简单。下面先通过一个例子来看一下如何创建一个单个的goroutine。


book/ch05/5.1/base/main.go
1. package main
2.
3. import (
4.     "fmt"
5.     "time"
6. )
7.
8. func HelloWorld()  {
9.     fmt.Println("Hello,World!")
10. }
11.
12. func main() {
13.     go HelloWorld()
14.     time.Sleep(1*time.Second)
15.     fmt.Println("The End!")
16. }
17. //以下为程序执行结果
18. Hello,World!
19. The End!

第8行至第10行,定义一个函数,函数里面仅仅是打印“Hello,World!”。

第12行至第16行,使用go关键字启动goroutine,注意第13行,这里启动了一个goroutine来运行HelloWorld函数。程序执行完成后可以看到第18行和第19行的输出。不过为什么要在第14行加上一个程序的休眠呢?因为如果不加程序休眠,很可能最终只打印“The End!”,这是缘于main函数在HelloWorld函数的goroutine执行完之前已经完成,只要main函数结束就会结束程序中所有的goroutine,故而为了安全加上了休眠。

这个例子非常简单,goroutine里面仅仅打印了一个字符串。如果goroutine里面的代码比较复杂,goroutine和main函数的goroutine之间的顺序能否有保证?再来看一个示例:


book/ch05/5.1/adv1/main.go
1. package main
2.
3. import (
4.     "fmt"
5.     "time"
6. )
7.
8. func main() {
9.     go func() {
10.         for i:=10;i<20;i++{
11.             fmt.Print(" ",i)
12.         }
13.     }()
14.     fmt.Println()
15.     for i:=0;i<10;i++{
16.         fmt.Print(" ",i)
17.     }
18.     time.Sleep(2*time.Second)
19. }
20.
21. //以下为程序执行结果
22. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
23. 0 10 11 1 2 3 4 5 6 7 8 9 12 13 14 15 16 17 18 19

第9行至第13行,定义了一个匿名函数,并且直接使用go关键字运行,函数的主体部分是打印从10到19的数字。

第14行,输出一个换行。

第15行至第17行,通过main函数打印0到9。

重点来看第22行和第23行的运行结果,这两次运行结果不同,就是因为main函数的goroutine(可以叫作主goroutine)和自启动goroutine之间是并发执行的,彼此没有关系,所以0到9是从小到大的顺序,10到19也保持着这个顺序,可是两个线程之间混在一起,没有同步关系,如果要实现同步关系需要进行额外的编程。

第14行的换行之所以在打印结果上没有体现出来,是因为这两次都是先执行了换行,所以从结果中看不出来。

上面的例子仅启动了一个goroutine,接下来启动多个goroutine,更直观地感受一下并发执行的效果,示例代码如下:


book/ch05/5.1/adv2/main.go
1. package main
2.
3. import (
4.     "fmt"
5.     "time"
6. )
7.
8. func main() {
9.     for i:=0;i<20;i++{
10.         go func(i int) {
11.             fmt.Print(" ",i)
12.         }(i)
13.     }
14.     time.Sleep(2*time.Second)
15.     fmt.Println("The End!")
16. }
17. //以下为程序两次执行结果
18.  3 1 11 5 12 13 14 0 2 7 4 17 16 18 9 8 6 10 19 15The End!
19.  1 10 0 15 11 12 13 14 5 2 3 4 17 16 7 6 18 8 9 19The End!

第9行至第13行,在循环语句中通过匿名函数启动了20个goroutine,每个goroutine打印0~19中的一个数字。结合第18行和第19行两次执行的结果来看,每次执行的打印顺序是不一样的,因为每次都是并发执行,不额外控制的话顺序就有随机性。

第14行的休眠是为了保证所有的goroutine都执行完成。有了第14行的休眠可以看到每次第15行的打印都会在最后。