如果这个问题太愚蠢,请道歉。我正在阅读goroutines Here的详细信息。根据那个页面,它说Goroutines are multiplexed onto a small number of OS threads, rather than a 1:1 mapping
,我用我有限的知识可以想到的是,产生的OS线程数量有限,其中可能使用用户空间线程或协同程序。它是否正确?如果是这样,如果我可以举一个例子,如果一个程序克隆4个OS线程,其中有多个用户空间线程,并且在所有这4个线程中发生了单个阻塞操作以及非阻塞操作,那么操作系统scheduler context-switch所有这些线程,因为用户空间线程对OS线程不透明?
出于好奇,是否有可能实现goroutines的C实现,这有助于理解内部结构?
答案 0 :(得分:3)
以下是我在阅读Go in Action
后理解的内容Goroutines在所谓的“逻辑处理器”(非物理处理器)内运行。每个逻辑处理器都绑定到一个OS线程。
在Go 1.5之后,逻辑处理器的数量等于可用物理处理器的数量。
Go调度程序智能地安排在每个逻辑处理器上运行多个goroutine
粗略图如下: -
OS线程------逻辑处理器------ Goroutine 1,Goroutine 2 ..... Goroutine n现在,其中一个Goroutines很可能会进行阻塞系统调用。发生这种情况时,
进行阻止调用的操作系统线程和Goroutine是 与逻辑处理器分离
此逻辑处理器现在没有操作系统线程。
Go调度程序创建一个新的OS线程,并将其附加到逻辑处理器。附加到逻辑处理器的其余goroutine现在继续运行。
分离的goroutine和与之关联的OS线程继续阻塞,等待系统调用返回。
当系统调用返回时,goroutine会重新连接到其中一个逻辑处理器,并被置于其运行队列中。
OS线程被“搁置以备将来使用”。我猜它被添加到某种线程池中。
如果goroutine进行网络I / O调用,则会以稍微不同的方式处理。
goroutine与逻辑处理器分离,并移动到集成网络轮询器。一旦轮询器说I / O操作准备就绪,goroutine就会重新连接到逻辑处理器来处理它。
- 现在,回答你的问题: - )
我不是专家,但根据上述内容,我认为这将会发生。
由于4个OS线程中的每一个上都有一个goroutine已经进行了阻塞系统调用,所有4个线程将从其逻辑处理器中分离出来,并且将继续阻塞,直到系统调用返回。 4个OS线程将与构成阻塞系统调用的相应goroutine相关联。
现在,这导致4个逻辑处理器(以及附加到它们的非阻塞goroutine)没有任何操作系统线程。
因此,GO调度程序创建了4个新的OS线程,并将逻辑处理器分配给这些线程。
-
从操作系统的角度来看,显然阻止调用的4个OS线程显然不能占用CPU时间,因为它们没有做任何事情。
因此它会将其上下文与其选择的其他非阻塞线程切换。