Go 戈朗:如何以最佳方式处理阻塞任务?

Go 戈朗:如何以最佳方式处理阻塞任务?,go,Go,众所周知,goroutine是同步但非阻塞的处理单元。 golang调度器可以很好地处理非阻塞任务,例如套接字、计时器、信号或来自char设备的其他事件 但是,阻止设备io或CPU敏感任务如何?它们在完成之前不能被中断,也不能被多路复用。运行goroutine的操作系统线程将冻结,直到goroutine返回或退出。在这种情况下,调度粒度变差 当然,您可以在代码中将任务拆分为更小的子任务,例如,不要一次复制1GB文件,而是先复制10MB,然后再复制10MB,等等,以便同一操作系统线程中的其他gor

众所周知,goroutine是同步但非阻塞的处理单元。 golang调度器可以很好地处理非阻塞任务,例如套接字、计时器、信号或来自char设备的其他事件

但是,阻止设备io或CPU敏感任务如何?它们在完成之前不能被中断,也不能被多路复用。运行goroutine的操作系统线程将冻结,直到goroutine返回或退出。在这种情况下,调度粒度变差

当然,您可以在代码中将任务拆分为更小的子任务,例如,不要一次复制1GB文件,而是先复制10MB,然后再复制10MB,等等,以便同一操作系统线程中的其他goroutine有机会运行。CPU绑定任务的另一个示例:将文件逐部分压缩并最终合并

但是这破坏了顺序编程的便利性,与操作系统线程上的操作系统调度相比,手动调度很难平均估计

nginx也有类似的问题,它是一个多工作进程程序,一个进程对应一个CPU核心,类似于GOMAXPROCS的最佳实践。它引入线程池来处理阻塞任务。也许这对戈朗也有好处。
我很好奇为什么golang没有OS线程API,这应该是goroutine的一个很好的补充,用于阻止任务。

Go特别选择了不直接向用户公开OS线程,而是选择了M:N线程模型。Go中的执行单元是goroutine,它将在N个OS线程上多路复用

在极少数情况下,CPU密集型计算不包含抢占点,操作系统线程不足,无法继续运行其他goroutine,您有2种选择;增加GOMAXPROCS,或插入
runtime.Gosched()
调用以使其他goroutine屈服

在阻塞系统的情况下,GO调度程序将自动调度一个新的OS线程(考虑一个SysCurl“阻塞”的时限是20US),并且由于非网络IO是一系列阻塞系统,它几乎总是被分配给一个专用的OS线程。由于Go已经使用了M:N线程模型,因此用户通常不知道底层调度程序的选择,并且可以编写与运行时使用异步IO相同的程序


有必要考虑使用异步文件IO,但是还有很多问题需要克服,比如Linux AIO API中的缺点、跨平台兼容性以及与所有可以用IO。/P>的各种文件系统和设备的交互。我想您误解了Galang-To调度程序的工作原理。(对阻塞和非阻塞调用感到困惑)。有点老了(特别是在go1.5之后),但它非常清楚地解释了调度程序的基本原理package@Elwinar,您误解了我的观点。请注意,我指的是块设备io和cpu敏感任务。用户空间调度依赖于这样一个事实,即任务是非块的,例如通过操作系统特定的机制进行套接字和多路复用ism,例如epoll。也就是说,goroutine之间的调度切换仅在EAGAIN/ewoodblock系统调用返回或goroutine显式生成时发生。但它不能中断阻塞系统调用。但是,操作系统内核可以这样做,例如,当您复制1GB文件时,linux内核将调用cond_resched()在每一个页面大小写一次迭代以放弃CPU。对于这类任务,有一个函数似乎被用来手动让位于调度程序。另外,从go1.2开始,有一个on函数调用。也就是说,对函数的每次调用都可能让位于调度程序。谢谢。文档中说“代表Go代码在系统调用中可以阻止的线程数量没有限制”(我还粗略地检查了源代码),因此,如果有许多长时间运行的阻塞系统调用,那么阻塞线程的数量可能会增加,直到超过系统限制并崩溃?@Kinglou:理论上是的,但是如果你试图同时读取数百万个文件,那么你可能做错了什么(更不用说在此期间您可能会遇到其他限制)。对本机主机解析程序的调用是个例外,在本机主机解析程序中,对正在进行的请求有限制,以限制可以使用的线程数。