Golang spec:
"go"语句启动一个函数调用的独立并发线程控制,或者说是在同一个地址空间中的"goroutine"。
有人能解释一下这个问题的基本概念吗?
"地址空间"是一个通用术语,可以适用于许多上下文环境:
地址空间是通过组合足够唯一标识符来使地址在特定地址空间内变得清晰明了而创建的。
Dave Cheney的演示文稿 "Five things that make Go fast" 阐述了在同一 进程 地址空间中使用goroutine所解决的主要问题:堆栈管理。
戴夫谈到了“地址空间”,首先提到了线程:
“因为进程在执行过程中可以在任何时候进行切换,所以操作系统需要存储所有这些寄存器的内容,因为它不知道哪些是当前正在使用的。”
这导致了线程的发展,它们在概念上与进程相同,但共享同一内存空间。
(所以这是关于内存的)
然后,戴夫说明了进程地址空间中的堆栈(由进程管理的地址):
![http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-39.jpg](https://istack.dev59.com/hA7p4.webp)
传统上,进程的地址空间中,堆位于内存底部,紧挨着程序(文本)并向上增长。栈位于虚拟地址空间的顶部,并向下增长。为了防止堆和栈互相覆盖造成灾难性后果,操作系统通常会在堆和栈之间放置一块不可写的内存区域,以确保如果它们发生碰撞,程序将会中止。对于线程,这可能会限制进程的堆大小。另请参见“
堆和栈是什么?在哪里?”。
![http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-41.jpg](https://istack.dev59.com/mrLK7.webp)
随着程序中线程数量的增加,可用地址空间的数量将减少。
goroutine使用一种不同的方法,同时仍然共享相同的进程地址空间:
那些goroutine的堆栈需求如何呢?Go编译器在每个函数调用中插入一个检查来检查函数是否有足够的堆栈运行,而不是使用守卫页面。如果没有足够的堆栈空间,运行时可以分配更多的堆栈空间。
由于这个检查,goroutines的初始堆栈可以变得更小,这反过来又使得Go程序员能够将goroutines视为廉价的资源。
Go 1.3引入了一种管理这些堆栈的新方式:
![http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-45.jpg](https://istack.dev59.com/RXvG6.webp)
如果goroutine的堆栈太小,不必添加和删除额外的堆栈段,而是会分配一个新的更大的堆栈。旧堆栈的内容将被复制到新堆栈中,然后goroutine将继续使用新的更大堆栈。在第一次调用H之后,堆栈将足够大,可用堆栈空间的检查将始终成功。
F()
应该使用更大的堆栈进行复制(如 https://dev59.com/yWIk5IYBdhLWcg3wPcCB#22684602 所示)。 - VonC