11.2.1 goroutine是什么

goroutine是Go并行设计的核心。goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。

11.2.2 创建goroutine

只需在函数调⽤语句前添加 go 关键字,就可创建并发执⾏单元。开发⼈员无需了解任何执⾏细节,调度器会自动将其安排到合适的系统线程上执行。

在并发编程里,我们通常想讲一个过程切分成几块,然后让每个goroutine各自负责一块工作。当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine。新的goroutine会用go语句来创建。

示例代码:

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func newTask() {
  7. i := 0
  8. for {
  9. i++
  10. fmt.Printf("new goroutine: i = %d\n", i)
  11. time.Sleep(1 * time.Second) //延时1s
  12. }
  13. }
  14. func main() {
  15. //创建一个 goroutine,启动另外一个任务
  16. go newTask()
  17. i := 0
  18. //main goroutine 循环打印
  19. for {
  20. i++
  21. fmt.Printf("main goroutine: i = %d\n", i)
  22. time.Sleep(1 * time.Second) //延时1s
  23. }
  24. }

11.2.3 主goroutine先退出

主goroutine退出后,其它的工作goroutine也会自动退出:

  1. func newTask() {
  2. i := 0
  3. for {
  4. i++
  5. fmt.Printf("new goroutine: i = %d\n", i)
  6. time.Sleep(1 * time.Second) //延时1s
  7. }
  8. }
  9. func main() {
  10. //创建一个 goroutine,启动另外一个任务
  11. go newTask()
  12. fmt.Println("main goroutine exit")
  13. }

11.2.4 runtime包

11.2.4.1 Gosched
runtime.Gosched() 用于让出CPU时间片,让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。

这就像跑接力赛,A跑了一会碰到代码runtime.Gosched() 就把接力棒交给B了,A歇着了,B继续跑。
示例代码:

  1. func main() {
  2. //创建一个goroutine
  3. go func(s string) {
  4. for i := 0; i < 2; i++ {
  5. fmt.Println(s)
  6. }
  7. }("world")
  8. for i := 0; i < 2; i++ {
  9. runtime.Gosched() //import "runtime"
  10. /*
  11. 屏蔽runtime.Gosched()运行结果如下:
  12. hello
  13. hello
  14. 没有runtime.Gosched()运行结果如下:
  15. world
  16. world
  17. hello
  18. hello
  19. */
  20. fmt.Println("hello")
  21. }
  22. }

11.2.4.2 Goexit

调用 runtime.Goexit() 将立即终止当前 goroutine 执⾏,调度器确保所有已注册 defer延迟调用被执行。

示例代码:

  1. func main() {
  2. go func() {
  3. defer fmt.Println("A.defer")
  4. func() {
  5. defer fmt.Println("B.defer")
  6. runtime.Goexit() // 终止当前 goroutine, import "runtime"
  7. fmt.Println("B") // 不会执行
  8. }()
  9. fmt.Println("A") // 不会执行
  10. }() //别忘了()
  11. //死循环,目的不让主goroutine结束
  12. for {
  13. }
  14. }

11.2.4.3 GOMAXPROCS

调用 runtime.GOMAXPROCS() 用来设置可以并行计算的CPU核数的最大值,并返回之前的值。

示例代码:

  1. func main() {
  2. //n := runtime.GOMAXPROCS(1) //打印结果:111111111111111111110000000000000000000011111...
  3. n := runtime.GOMAXPROCS(2) //打印结果:010101010101010101011001100101011010010100110...
  4. fmt.Printf("n = %d\n", n)
  5. for {
  6. go fmt.Print(0)
  7. fmt.Print(1)
  8. }
  9. }

在第一次执行(runtime.GOMAXPROCS(1))时,最多同时只能有一个goroutine被执行。所以
会打印很多1。过了一段时间后,GO调度器会将其置为休眠,并唤醒另一个goroutine,这时候就开始打印很多0了,在打印的时候,goroutine是被调度到操作系统线程上的。

在第二次执行(runtime.GOMAXPROCS(2))时,我们使用了两个CPU,所以两个goroutine可以一起被执行,以同样的频率交替打印0和1。

作者:admin  创建时间:2018-06-18 06:45
 更新时间:2018-06-18 06:50
上一篇:
下一篇: