Goroutines vs 多线程

前言


Golang是由Google开发,天生支持并发的语言。Go有一个goroutine的机制,当我们在调用函数前加上go关键字,那么就会创建一个goroutine来异步执行该函数,如:go foo(),以此来实现并发的功能。本文,我们将讨论一下goroutine和线程的区别。

大小可调整的栈


线程可以在启动前设置栈的大小,启动后,线程的栈大小就固定了,所以带来的弊端就是浪费内存空间,因为很多时候并不需要这么多内存。
相比于线程,goroutine的栈空间是可调整的,goroutine创建时,通常只会分配2KB大小的栈,随着goroutine的运行,比如不断的递归,创建变量等,相应的栈空间也会增大,反之也会按需减小,一个goroutine最大可分配栈空间通常为1GB

goroutine 的调度


线程由系统内核进行调度,系统为了实现并发,会不断的切换线程的执行,由此会带来线程的上下文切换。
Go的运行时有一套自己的调度系统,使用m:n策略,既复用mgoroutinen个线程,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

附录


  1. https://golang.google.cn
  2. http://www.gopl.io