gpt4 book ai didi

go - goroutine 在什么时候可以 yield?

转载 作者:行者123 更新时间:2023-12-02 11:25:32 24 4
gpt4 key购买 nike

我正在尝试更好地了解 Go 程序中如何安排 goroutines,尤其是在哪些点上它们可以屈服于其他 goroutines。我们知道 goroutine 在 syscals 上产生会阻塞它,但显然这不是全部。

This question引起了一些类似的担忧,评分最高的答案说 goroutine 也可以打开函数调用,因为这样做会调用调度程序来检查堆栈是否需要增长,但它明确表示

If you don't have any function calls, just some math, then yes,goroutine will lock the thread until it exits or hits something thatcould yield execution to others.

我写了一个简单的程序来验证和证明:

package main

import "fmt"

var output [30]string // 3 times, 10 iterations each.
var oi = 0

func main() {
runtime.GOMAXPROCS(1) // Or set it through env var GOMAXPROCS.
chanFinished1 := make(chan bool)
chanFinished2 := make(chan bool)

go loop("Goroutine 1", chanFinished1)
go loop("Goroutine 2", chanFinished2)
loop("Main", nil)

<- chanFinished1
<- chanFinished2

for _, l := range output {
fmt.Println(l)
}
}

func loop(name string, finished chan bool) {
for i := 0; i < 1000000000; i++ {
if i % 100000000 == 0 {
output[oi] = name
oi++
}
}

if finished != nil {
finished <- true
}
}

注意:我知道在数组中放入一个值并在不同步的情况下递增 oi 不太正确,但我想让代码简单并且没有可能导致的事情交换。毕竟,可能发生的最糟糕的事情是在不推进索引(覆盖)的情况下输入一个值,这没什么大不了的。

不同于this answer ,我避免使用作为 goroutine 启动的 loop() 函数的任何函数调用(包括内置 append()),我也明确设置 GOMAXPROCS=1 根据documentation :

limits the number of operating system threads that can execute user-level Go code simultaneously.

然而,在输出中我仍然看到消息 Main/Goroutine 1/Goroutine 2 交错,表示以下之一:

  • goroutine 的执行中断并且 goroutine 放弃在某些时刻控制;
  • GOMAXPROCS 没有像文档,启动更多操作系统线程来安排 goroutine。

answer不完整,或者自 2016 年以来发生了一些变化(我在 Go 1.13.5 和 1.15.2 上测试过)。

如果问题得到了回答,我很抱歉,但我既没有找到关于为什么这个特定示例产生控制的解释,也没有找到 goroutine 通常产生控制的点(阻塞系统调用除外)。

注意:这个问题纯粹是理论性的,我现在不打算解决任何实际任务,但总的来说,我假设知道 goroutine 可以让步的点和不能让我们避免重复使用的点同步原语。

最佳答案

引入了 Go 版本 1.14 asynchronous preemption:

Goroutines are now asynchronously preemptible. As a result, loops without function calls no longer potentially deadlock the scheduler or significantly delay garbage collection. This is supported on all platforms except windows/arm, darwin/arm, js/wasm, and plan9/*.

Are channel sends preemption points for goroutine scheduling? 中的回答, Go 的抢占点可能会从一个版本到下一个版本发生变化。异步抢占只是在几乎所有地方添加可能的抢占点。

您对 output 数组的写入不同步并且您的 oi 索引不是原子的,这意味着我们无法真正确定输出会发生什么大批。当然,用互斥锁给它增加原子性会引入协作调度点。虽然这些不是协作调度切换的来源(这必须根据您的输出发生),但它们确实会扰乱我们对程序的理解。

output 数组保存字符串,使用字符串可以调用垃圾回收系统,垃圾回收系统可以使用锁并引起调度切换。因此,这是 Go-1.14 之前的实现中调度切换的最可能原因。

关于go - goroutine 在什么时候可以 yield?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64113394/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com