1、goroutine的基本语法

goroutine 是Go语言中的轻量级线程实现,由Go运行时(runtime)管理。Go程序会智能的将 goroutine中的任务合理地分配给每个CPU。Go程序从main包的main()函数开始,在程序启动时,Go程序就会为main()函数创建一个默认的 goroutine。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func foo() {
  7. fmt.Println("foo 函数开始")
  8. time.Sleep(time.Second * 2)
  9. fmt.Println("foo 函数结束")
  10. }
  11. func bar() {
  12. fmt.Println("bar 函数开始")
  13. time.Sleep(time.Second * 3)
  14. fmt.Println("bar 函数结束")
  15. }
  16. func main() {
  17. startTime := time.Now().Unix()
  18. go foo() // 开启并行任务 foo
  19. go bar() // 开启并行任务 bar
  20. endTime := time.Now().Unix()
  21. fmt.Println("消费时间,", endTime-startTime)
  22. }

如图:
12.2、goroutine的基本使用 - 图1
这种情况是主线程跑完就结束,并没有等待并发任务。

2、sync.WaitGroup

Go语言中可以使用 sync.WaitGroup来实现并发任务的同步。sync.WaitGroup有以下几个方法:

方法名 功能
(wg * WaitGroup) Add(delta int) 计数器+delta
(wg *WaitGroup) Done() 计数器-1
(wg *WaitGroup) Wait() 阻塞直到计数器变为0

sync.WaitGroup 内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N个并发任务时,就将计数器的值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func foo() {
  8. defer wg.Done() // 3. 计数器减去 1
  9. fmt.Println("foo 函数开始")
  10. time.Sleep(time.Second * 2)
  11. fmt.Println("foo 函数结束")
  12. }
  13. func bar() {
  14. defer wg.Done() // 3. 计数器减去 1
  15. fmt.Println("bar 函数开始")
  16. time.Sleep(time.Second * 3)
  17. fmt.Println("bar 函数结束")
  18. }
  19. var wg sync.WaitGroup // 1. 定义同步等待变量
  20. func main() {
  21. startTime := time.Now().Unix()
  22. wg.Add(2) // 2. 添加计数器
  23. funcData := [2]func(){foo, bar}
  24. for _, v := range funcData {
  25. go v()
  26. }
  27. wg.Wait() // 4.等待并发完毕
  28. endTime := time.Now().Unix()
  29. fmt.Println("消费时间,", endTime-startTime)
  30. }

3、GOMAXPROCS

Go运行时的调度器使用 GOMAXPROCS 参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中n)。
Go语言中可以通过 runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。
Go1.5版本之前,默认使用的是单核执行。Go1.5版本之后,默认使用全部CPU逻辑核心数。
我们可以通过将任务分配到不同的CPU逻辑核心上实现并行的效果,这里举个例子:

  1. func main() {
  2. startTime := time.Now().Unix()
  3. wg.Add(2)
  4. fmt.Println(runtime.NumCPU()) // 查看cpu的个数
  5. runtime.GOMAXPROCS(1) // 并发跑1核
  6. funcData := [2]func(){foo, bar}
  7. for _, v := range funcData {
  8. go v()
  9. }
  10. wg.Wait()
  11. endTime := time.Now().Unix()
  12. fmt.Println("消费时间,", endTime-startTime)
  13. }