在Select语句的默认情况下使用`runtime.Gosched()`有意义吗?

huangapple go评论73阅读模式
英文:

Does using `runtime.Gosched()` in the default case of a Select statement make any sense?

问题

根据Go的文档,Gosched函数会让出处理器,允许其他goroutine运行。它不会挂起当前的goroutine,所以执行会自动恢复。

根据这个定义,如果我有一系列长时间运行的goroutine被创建并发执行,将下面的select语句写成这样是否有优势:

for {
    select {
        case msg := <-msgch : 
            fmt.Println(msg)
        default: 
            runtime.Gosched()
    }
}

根据文档,我认为这段代码可以导致更多的goroutine运行。我的假设正确吗?

英文:

Go's documentation says that

> Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically.

Based on that definition if I have a series of long running go routines being created and executed concurrently, would it be advantageous to write a select statement the following way:

for {
    select {
        case msg := &lt;- msgch : 
            fmt.Println(msg)
        default: 
            runtime.Gosched()
    }
} 

I assume based on the documentation, this code can result in more go routines being run. Is my assumption correct?

答案1

得分: 4

不,这里不需要使用runtime.Gosched,因为在Go中,当goroutine在等待通道或等待I/O时,它会自动让其他goroutine运行。这个特性从Go 1.0版本就存在了。

Go 1.2中,Go运行时调度器在调用函数时添加了自动抢占点。在此之前,如果你有一个CPU密集型的循环(即使有函数调用),它可能会饿死调度器,你可能需要使用runtime.Gosched

然后在Go 1.14中,他们进一步改进了运行时的这个方面,即使是没有函数调用的紧密CPU密集型循环也会被自动抢占。

因此,在任何Go版本中,当你只是在等待通道或I/O时,不需要调用runtime.Gosched;在1.14之前,如果你进行长时间的计算,可能需要调用它。但是在Go 1.14+中,我不认为你需要手动调用它。


如果我正在审查你的实际代码,我建议将其改为简单的for ... range循环:

for msg := range msgCh {
    fmt.Println(msg)
}

这将等待每个消息的到来并打印它,如果通道关闭,则停止。然而,如果你正在等待另一个通道或完成信号(例如上下文),你可能需要使用switch。类似这样:

for {
    select {
        case msg := <-msgCh:
            fmt.Println(msg)
        case <-ctx.Done():
            return
    }
}
英文:

No, it isn't necessary here, because whenever Go is waiting on a channel or waiting for I/O, it allows other goroutines to run automatically. That's been the case since Go 1.0.

In Go 1.2 the Go runtime's scheduler added automatic preemption points whenever you called a function. Prior to that if you had a CPU-bound loop (even with a function call) it could starve the scheduler and you might need runtime.Gosched.

And then in Go 1.14, they made this aspect of the runtime even better, and even tight CPU-bound loops with no functions calls are automatically pre-empted.

So with any Go version, you don't need to call runtime.Gosched when you're just waiting on a channel or on I/O; before 1.14, you may have wanted to call it if you were doing a long-running calculation. But with Go 1.14+, I don't see why you'd ever need to call it manually.


If I was reviewing your actual code, I'd suggest changing it to a simple for ... range loop:

for msg := range msgCh {
    fmt.Println(msg)
}

This will wait for each message to come in and print it, and stop if/when the channel is closed. However, you would want a switch if you're waiting on another channel or done signal, for example a context. Something like this:

for {
    select {
        case msg := &lt;- msgCh:
            fmt.Println(msg)
        case &lt;-ctx.Done():
            return
    }
}

答案2

得分: 1

使用runtime.Gosched()是否有任何意义?

不。基本上不需要或没有意义使用Gosched。

英文:

> Does using runtime.Gosched() [anywhere] make any sense?

No. It basically is never needed or sensible to use Gosched.

huangapple
  • 本文由 发表于 2022年6月10日 10:58:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/72568653.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定