goroutine的基本使用
2.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)
fmt.Println("foo end")
}
func bar() {
fmt.Println("bar")
time.Sleep(time.Second*2)
fmt.Println("bar end")
}
func main() {
go foo()
bar()
}
2.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"
)
var wg sync.WaitGroup
func foo() {
defer wg.Done()
fmt.Println("foo")
time.Sleep(time.Second)
fmt.Println("foo end")
}
func bar() {
defer wg.Done()
fmt.Println("bar")
time.Sleep(time.Second*2)
fmt.Println("bar end")
}
func main() {
start:=time.Now()
wg.Add(2)
go foo()
go bar()
wg.Wait()
fmt.Println("程序结束,运行时间为",time.Now().Sub(start))
}
2.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逻辑核心上实现并行的效果,这里举个例子:
package main
import (
"fmt"
"runtime"
"sync"
)
var wg sync.WaitGroup
func foo() {
for i := 1; i < 10; i++ {
fmt.Println("A:", i)
//time.Sleep(time.Millisecond*20)
}
wg.Done()
}
func bar() {
for i := 1; i < 10; i++ {
fmt.Println("B:", i)
//time.Sleep(time.Millisecond*30)
}
wg.Done()
}
func main() {
wg.Add(2)
fmt.Println(runtime.NumCPU())
runtime.GOMAXPROCS(1)// 改为4
go foo()
go bar()
wg.Wait()
}