Goroutines vs 多线程
前言
Golang是由Google
开发,天生支持并发的语言。Go
有一个goroutine
的机制,当我们在调用函数前加上go
关键字,那么就会创建一个goroutine
来异步执行该函数,如:go foo()
,以此来实现并发的功能。本文,我们将讨论一下goroutine
和线程的区别。
大小可调整的栈
线程可以在启动前设置栈的大小,启动后,线程的栈大小就固定了,所以带来的弊端就是浪费内存空间,因为很多时候并不需要这么多内存。
相比于线程,goroutine
的栈空间是可调整的,goroutine
创建时,通常只会分配2KB
大小的栈,随着goroutine
的运行,比如不断的递归,创建变量等,相应的栈空间也会增大,反之也会按需减小,一个goroutine
最大可分配栈空间通常为1GB
。
goroutine
的调度
线程由系统内核进行调度,系统为了实现并发,会不断的切换线程的执行,由此会带来线程的上下文切换。
而Go
的运行时有一套自己的调度系统,使用m:n
策略,既复用m
个goroutine
到n
个线程,Go
的调度器类似于内核调度器,区别是它仅管理单个Go
程序创建的goroutine
。
相比于线程,Go
的调度器并不会周期性的被硬件定时器调用,而是Go
自己来处理,比如,当一个goroutine
调用time.Sleep
或阻塞与通道、互斥操作时,调度器会将goroutine
休眠,然后运行另外的goroutine
,这些并不需要内核的上下文切换,所以重新调度一个goroutine
的开销会小于重新调度线程的开销。
GOMAXPROCS
Go
的调度器使用一个名为GOMAXPROCS
的参数来确定真正的线程数,其默认值为机器的CPU
的数量,如一台机器有8个CPU
,那么调度器会同时创建8个线程(GOMAXPROCS
就是如上所说m:n
中的n
)。休眠或阻塞中goroutine
不需要线程,但是对于阻塞在I/O
或系统调用、正在调用非Go
函数的goroutine
来说,需要一个系统线程,不过不占用GOMAXPROCS
。