众所周知,goroutine是同步但非阻塞的处理单元。 golang调度程序处理非阻塞任务,例如来自char设备的套接字,定时器,信号或其他事件。
但阻止设备io或CPU敏感任务怎么样?他们不能被打断直到完成,而不是多路复用。运行goroutine的OS线程将冻结,直到goroutine返回或产生。在这种情况下,调度粒度变差。
当然,您可以将任务拆分为代码中较小的子任务,例如,不要一次复制1GB文件,而是先复制10MB,产生并复制另外10MB等,以便同一OS线程中的其他goroutine有机会运行。 CPU绑定任务的另一个例子:逐个压缩文件并最终合并它们。
但这打破了顺序编程的便利性,与OS线程上的OS调度相比,手动调度很难均匀估计。
nginx有类似的问题,它是一个多工作进程程序,一个CPU核心的进程,类似于GOMAXPROCS的最佳实践。它引入了线程池来处理阻塞任务。也许这对golang也有好处。 我很好奇为什么golang没有操作系统线程API,这应该是阻止任务的goroutine的良好补充。
答案 0 :(得分:4)
Go专门选择不直接向用户公开操作系统线程,而是选择了M:N线程模型。您在Go中执行的单位是goroutine,它将在N个OS线程上多路复用。
在罕见的情况下,您的CPU密集型计算不包含抢占点,并且OS线程不足以继续运行其他goroutine,您有2个选择;增加GOMAXPROCS,或插入runtime.Gosched()
调用以屈服于其他goroutines。
在阻塞系统调用的情况下,Go调度程序将自动调度一个新的OS线程(考虑系统调用的时间限制"阻塞"已经是20us),并且因为非网络IO是一个系列阻止系统调用,它几乎总是被分配给专用的OS线程。由于Go已经使用了M:N线程模型,因此用户通常不会意识到底层调度程序的选择,并且可以像运行时使用异步IO一样编写程序。
使用异步文件IO需要考虑open issue,但还有许多问题需要克服,例如Linux aio api中的缺点,跨平台兼容性以及与所有各种文件系统和设备的交互你可以做IO。