【发布时间】:2013-08-06 03:50:44
【问题描述】:
在 Google I/O 2012 演示文稿Go Concurrency Patterns 中,Rob Pike 提到多个 goroutines 可以存在于一个线程中。这是否意味着它们被实现为coroutines?如果没有,它们是如何实施的?欢迎提供源代码链接。
【问题讨论】:
标签: multithreading go goroutine
在 Google I/O 2012 演示文稿Go Concurrency Patterns 中,Rob Pike 提到多个 goroutines 可以存在于一个线程中。这是否意味着它们被实现为coroutines?如果没有,它们是如何实施的?欢迎提供源代码链接。
【问题讨论】:
标签: multithreading go goroutine
IMO,协程意味着支持 explicit 将控制权转移到另一个协程的方法。也就是说,程序员在决定协程何时应该暂停执行并将其控制权传递给另一个协程(通过调用它或通过返回/退出(通常称为屈服))时,以某种方式对协程进行编程。
Go 的“goroutines”是另一回事:它们隐式在 goroutine 即将休眠时发生的某些不确定点1交出控制权在一些(外部)资源上,如 I/O 完成、通道发送等。这种方法与通过通道共享状态相结合,使程序员能够将程序逻辑编写为一组 sequential 轻量级进程,从而消除协程和基于事件的方法常见的意大利面条代码问题。
关于实现,我认为它们与(不幸的是不太知名)"State Threads" library 非常相似,只是相当低级(因为 Go 不依赖 libc 或类似的东西和谈话直接到操作系统内核)——您可以阅读 ST 库的介绍性论文,其中对概念进行了很好的解释。
1 事实上,这些点不如协程确定,但比preemptive multitasking 下的真正操作系统线程更确定,其中每个线程都可能在任何给定点被内核挂起时间和线程控制的流程。
2021 年 5 月 28 日更新: 实际上,从 Go 1.14 开始,goroutines are scheduled (almost) preemptively。
不过应该注意的是,它仍然不是 典型内核对其管理的线程进行的硬核抢占,但它比以前更接近了;至少,一旦 goroutine 进入繁忙循环,它就不可能成为不可抢占的。
【讨论】:
不完全是。 Go 常见问题解答部分 Why goroutines instead of threads? 解释说:
Goroutines 是使并发易于使用的一部分。这个想法已经存在了一段时间,它是将独立执行的函数(协程)多路复用到一组线程上。当协程阻塞时,例如通过调用阻塞系统调用,运行时会自动将同一操作系统线程上的其他协程移动到不同的可运行线程,这样它们就不会被阻塞。程序员看不到这些,这就是重点。结果,我们称之为 goroutine,可能非常便宜:除了堆栈内存之外,它们的开销很小,只有几千字节。
为了使堆栈变小,Go 的运行时使用可调整大小的有界堆栈。一个新创建的 goroutine 有几千字节,这几乎总是足够的。如果不是,则运行时会自动增长(和缩小)用于存储堆栈的内存,从而允许许多 goroutines 存在于适度的内存中。 CPU 开销平均每个函数调用大约三个廉价指令。在同一个地址空间中创建数十万个 goroutine 是很实用的。如果 goroutine 只是线程,那么系统资源会以更少的数量耗尽。
【讨论】:
goroutine 是适当的协程还是类似的东西经常在https://groups.google.com/forum/?fromgroups=#!forum/golang-nuts 上讨论。有些人可能会争论这些微妙之处,但对于大多数人来说:goroutine 是协程。
看看https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit 了解调度程序的工作原理。
【讨论】:
Goroutine 是一个单独的执行“线程”。它是 IMO 不能真正与协程相提并论。在第一个近似值中,goroutines 可以由 real OS 线程实现。 AFAIK,早期版本的 gccgo 就是这种情况。另一个区别是 goroutine 可以被抢占。
当前的 Go 编译器将 goroutine 实现为非常轻量级的用户空间“线程”。一个明显的特征,例如。 green threads 是 goroutine 可以切换到不同的操作系统线程。
我想你可以在这里找到一些相关的兴趣点:proc.c
【讨论】:
preempted 是什么意思?