线程其实分为两种:一种是传统的操作系统线程,另一种是编程语言实现的用户态线程,也叫coroutine,在Go中就是goroutine。因此,goroutine的存在必然是为了改变操作系统线程的一些缺点——过于沉重。过重表现在以下几个方面:第一:创建和切换过重操作系统线程的创建和切换需要进入内核,进入内核的性能开销比较大,开销比较大;第二:内存占用太大。一方面,为了避免操作系统线程栈在极端情况下溢出,内核会在创建操作系统时分配一大块栈内存(虚拟地址空间,内核一开始不会分配那么多)线。物理内存),但在大多数情况下,系统线程远没有使用那么多内存,从而导致浪费;另一方面,栈内存空间一旦创建并初始化,其大小就不能再改变,这意味着系统线程栈在某些特殊场景下仍然存在溢出的风险。相比之下,用户态goroutine要轻很多:goroutine是一个用户态线程,它的创建和切换都是在用户代码中完成的,不需要进入操作系统内核,所以它的开销比系统线程的创建和切换要小得多;当goroutine启动时,默认的堆栈大小只有2k,这在大多数情况下已经足够了。即使不够用,goroutine栈也会自动扩容。同时,如果栈太大造成浪费,也可以自动收缩,这样就不存在栈溢出的风险,不会造成栈内存空间的大量浪费。
