【问题标题】:Are channel sends preemption points for goroutine scheduling?通道是否为 goroutine 调度发送抢占点?
【发布时间】:2016-03-15 12:36:43
【问题描述】:

根据我对 Go 调度器的理解,Go 调度算法是partially preemptive:goroutine 切换发生在 goroutine 调用函数或阻塞 I/O 时。

向频道发送消息时是否会发生 goroutine 切换?

// goroutine A
ch <- message
// some additional code without function calls

// goroutine B
message := <- ch

在上面的代码中,我想让A中ch &lt;- message之后的代码在切换到B之前执行,这样可以保证吗?还是在 A 在ch 上发送消息后立即安排 B?

【问题讨论】:

  • 依赖运行时的实现细节充满了危险。我会重新考虑你的代码。
  • 很多时候没有运行时抢占。 Goroutines A 和 B 可能运行在由内核抢占的不同操作系统线程中,或者完全不同的 CPU 内核中。

标签: go scheduling channel goroutine


【解决方案1】:

A 的通道发送可能会阻塞,此时它会交给调度程序,并且您无法保证 A 何时会再次接收控制。它可能在您对 B 感兴趣的代码之后。因此,即使使用 GOMAXPROCS=1,示例代码也存在问题。

退一步:当抢占发生时是一个实现细节;它在过去发生了变化(函数调用并不总是有抢占的机会)并且将来可能会发生变化。就memory model 而言,如果您的程序依赖于关于代码何时执行的事实,而这些事实恰好在今天是正确的,但并不能保证,那么它就是不正确的。如果您想阻止 B 中的某些代码运行,直到 A 执行某些操作,您需要找到一种使用通道或sync 原语来安排它的方法。

正如用户 JimB 所指出的,您甚至不需要考虑抢占,因为示例代码会出现问题。 A 和 B 可以同时运行在不同的 CPU 内核上,B 中接收后的代码可以运行,而 A 中发送后的代码正在运行。

【讨论】:

    【解决方案2】:

    我对语言和运行时的实际理解表明,如果您在 ch &lt;- message 之后和调用 goroutine B 之前没有显式阻塞,您无法保证 A 将在 B 之前完成或运行。我不知道这是如何实际实现的但我也不在乎,因为我从表面上接受 goroutine 抽象。不要依赖程序中的巧合功能。仅以您的示例为例,我的建议是将通道传递给 goroutine A,然后阻止等待接收它以序列化 A 和 B。

    【讨论】:

      猜你喜欢
      • 2018-09-16
      • 2013-02-22
      • 1970-01-01
      • 1970-01-01
      • 2019-05-23
      • 2014-11-18
      • 1970-01-01
      • 1970-01-01
      • 2018-09-29
      相关资源
      最近更新 更多