1、goroutine的基本语法
goroutine 是Go语言中的轻量级线程实现,由Go运行时(runtime)管理。Go程序会智能的将 goroutine中的任务合理地分配给每个CPU。Go程序从main包的main()函数开始,在程序启动时,Go程序就会为main()函数创建一个默认的 goroutine。
package main
import (
"fmt"
"time"
)
func foo() {
fmt.Println("foo 函数开始")
time.Sleep(time.Second * 2)
fmt.Println("foo 函数结束")
}
func bar() {
fmt.Println("bar 函数开始")
time.Sleep(time.Second * 3)
fmt.Println("bar 函数结束")
}
func main() {
startTime := time.Now().Unix()
go foo() // 开启并行任务 foo
go bar() // 开启并行任务 bar
endTime := time.Now().Unix()
fmt.Println("消费时间,", endTime-startTime)
}
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时,表示所有并发任务已经完成。
package main
import (
"fmt"
"sync"
"time"
)
func foo() {
defer wg.Done() // 3. 计数器减去 1
fmt.Println("foo 函数开始")
time.Sleep(time.Second * 2)
fmt.Println("foo 函数结束")
}
func bar() {
defer wg.Done() // 3. 计数器减去 1
fmt.Println("bar 函数开始")
time.Sleep(time.Second * 3)
fmt.Println("bar 函数结束")
}
var wg sync.WaitGroup // 1. 定义同步等待变量
func main() {
startTime := time.Now().Unix()
wg.Add(2) // 2. 添加计数器
funcData := [2]func(){foo, bar}
for _, v := range funcData {
go v()
}
wg.Wait() // 4.等待并发完毕
endTime := time.Now().Unix()
fmt.Println("消费时间,", endTime-startTime)
}
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逻辑核心上实现并行的效果,这里举个例子:
func main() {
startTime := time.Now().Unix()
wg.Add(2)
fmt.Println(runtime.NumCPU()) // 查看cpu的个数
runtime.GOMAXPROCS(1) // 并发跑1核
funcData := [2]func(){foo, bar}
for _, v := range funcData {
go v()
}
wg.Wait()
endTime := time.Now().Unix()
fmt.Println("消费时间,", endTime-startTime)
}