goroutine
Go语言的并发通过goroutine
实现,其概念类似于线程,属于用户态的线程,我们可以创建很多个goroutine
并发工作。goroutine
是由Go的运行时(runtime
)来调度和管理的。
使用方式
启动goroutine
只需要在调用的函数前加上go
关键字,函数可以是普通函数也可以是匿名函数。
在程序启动时,Go程序会为main()
函数创建一个默认的goroutine
。它会在main()
函数返回的时候结束,此时所有在main()
函数中启动的goroutine
不管有没有运行完都会一同结束。
如下例:
func f1() {
fmt.Println("我是f1")
}
func main() {
go f1()
fmt.Println("我是main")
}
输出:
我是main
可以发现,函数f1
并没有打印结果,这是因为goroutine
启动需要一定的时间,而例子中的main()
函数早就结束了,在其中启动的goroutine
也随之结束。
再看下例:
func f1() {
fmt.Println("我是f1")
}
func main() {
go f1()
fmt.Println("我是main")
time.Sleep(time.Second)
}
输出:
我是main
我是f1
我们让main()
函数简单的睡眠1秒钟,让goroutine
有时间执行完。因为goroutine
启动需要时间,所以这里"我是main"
先输出。
启动多个goroutine
当我们启动多个goroutine
时,我们难以确定要让main()
函数睡眠多久,这时我们可以使用sync.WaitGroup
来实现goroutine
的同步。
sync.WaitGroup介绍
WaitGroup
对象内部有一个计数器,最初从0
开始,它有三个方法:Add()
, Done()
, Wait()
用来控制计数器的数量。Add(n)
每次让计数器+n
,n
不能让计数器为负,否则会引发panic
;Done()
每次让计数器-1
,wait()
会阻塞代码的运行,直到计数器的值为0
。
WaitGroup
对象是结构体类型,传参时需要使用地址,否则进程会死锁。
示例
func f(i int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("我是f%d\n", i)
}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go f(i, &wg)
}
fmt.Println("我是main")
wg.Wait()
}
输出:
我是f8
我是f3
我是f9
我是f5
我是f6
我是f7
我是f1
我是f0
我是f2
我是main
我是f4
每次的输出顺序都可能不同,因为并发执行时goroutine
的调度是随机的。
设置程序占用的最大逻辑核心数
Go语言中可以通过runtime.GOMAXPROCS()
函数设置当前程序并发时占用的CPU逻辑核心数,Go1.5版本后默认为当前机器的CPU逻辑核心数。
使用方式:
runtime.GOMAXPROCS(4) // 将程序可占用的最大核心数设置为4个,最多4个任务并行
通过runtime.NumCPU()
函数可以获取当前机器的CPU逻辑核心数。