如果 goroutines 涉及用户空间线程,阻塞操作是否会导致整个线程的上下文切换?

If goroutines involve userspace threads, can a blocking operation leads to context switch of the entire thread?

如果这个问题太愚蠢,我们深表歉意。我正在阅读 goroutines Here 的详细信息。根据那个页面,它说 Goroutines are multiplexed onto a small number of OS threads, rather than a 1:1 mapping ,据我所知,我所能想到的是,生成的 OS 线程数量有限,在其中,它可能正在使用用户空间线程或协程。这个对吗?如果是这样,我可以举个例子,如果一个程序克隆了 4 OS 个线程,其中有多个用户空间线程,并且在所有这 4 个线程中碰巧有一个阻塞操作以及非阻塞操作,OS 调度程序是否会上下文切换所有这些线程,因为用户空间线程对 OS 线程不透明?

出于好奇,是否有 goroutines 的可能 C 实现,这有助于理解内部结构?

下面是我看完后的理解Go in Action

Goroutines 运行 在所谓的 "logical processors"(不是物理处理器)中。这些逻辑处理器中的每一个都绑定到一个 OS 线程。

在 Go 1.5 之后,逻辑处理器的数量等于可用物理处理器的数量。

Go 调度程序智能地调度 运行 多个 goroutines 在每个逻辑处理器上

粗略图如下:-

OS 线程 ------ 逻辑处理器 ------ Goroutine 1, Goroutine 2..... Goroutine n

现在,很可能其中一个 Goroutine 进行了阻塞系统调用。发生这种情况时,

  1. OS线程和进行阻塞调用的Goroutine是 与逻辑处理器分离

    此逻辑处理器现在没有 OS 线程。

  2. Go 调度程序创建一个新的 OS 线程,并将其附加到逻辑处理器。附加到逻辑处理器的剩余 goroutines,现在继续 运行.

  3. 分离的 goroutine 和它关联的 OS 线程继续阻塞,等待对 return.

  4. 的系统调用
  5. 当系统调用returns时,goroutine被重新附加到其中一个逻辑处理器,并被放入其运行队列中。

  6. OS 线程是 "put aside for future use"。我猜它被添加到某种线程池中。

如果 goroutine 进行网络 I/O 调用,其处理方式略有不同。

goroutine 与逻辑处理器分离,并移动到集成网络轮询器。一旦轮询器说 I/O 操作准备就绪,goroutine 就会重新连接到逻辑处理器来处理它。

-- 现在,回答你的问题:-)

我不是专家,但根据上述内容,我认为会发生这种情况。

由于 4 个 OS 线程中每个线程上的一个 goroutine 都进行了阻塞系统调用,因此所有 4 个线程都将与其逻辑处理器分离,并将继续阻塞直到系统调用 return. 4 OS 个线程将与进行阻塞系统调用的相应 goroutine 相关联。

现在,这导致 4 个逻辑处理器(以及附加到它们的非阻塞 goroutines)没有任何 OS 个线程。

因此,GO 调度程序创建了 4 个新的 OS 线程,并将逻辑处理器分配给这些线程。

--

从 OS 的角度来看,进行阻塞调用的 4 个 OS 线程显然不能占用 CPU 时间,因为它们什么都不做.

因此它将与它选择的其他一些非阻塞线程切换它们的上下文。