Why do we need to call runtime.Gosched after call to atomic.AddUint64 and other similar atomic ops?

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

Why do we need to call runtime.Gosched after call to atomic.AddUint64 and other similar atomic ops?

问题

通过*Go by Example: Atomic Counters*。代码示例在调用atomic.AddUint64之后调用了runtime.Gosched

调用atomic.AddUint64是为了

> 确保此goroutine不会饿死调度器

不幸的是,我发现这个解释并不充实和令人满意。

我尝试运行了示例代码(为了简洁起见,删除了注释):

package main

import "fmt"
import "time"
import "sync/atomic"
import "runtime"

func main() {

    var ops uint64 = 0

    for i := 0; i < 50; i++ {
        go func() {
            for {
                atomic.AddUint64(&ops, 1)

                runtime.Gosched()
            }
        }()
    }

    time.Sleep(time.Second)

    opsFinal := atomic.LoadUint64(&ops)
    fmt.Println("ops:", opsFinal)
}

没有使用runtime.Gosched()go run conc.go),即使我将循环从50减少到1,程序也从未退出。

问题:

在调用atomic.AddUint64之后,在底层发生了什么,以至于需要调用runtime.Goschedruntime.Gosched是如何解决这个问题的?我在sync/atomic的文档中没有找到任何关于这方面的提示。

英文:

Going through Go by Example: Atomic Counters. The code example calls runtime.Gosched after calling atomic.AddUint64.

atomic.AddUint64 is called to

> ensure that this goroutine doesn’t starve the scheduler

Unfortunately, I am finding the explanation not so meaty and satisfying.

I tried running the sample code (comments removed for conciseness):

package main

import &quot;fmt&quot;
import &quot;time&quot;
import &quot;sync/atomic&quot;
import &quot;runtime&quot;

func main() {

    var ops uint64 = 0

    for i := 0; i &lt; 50; i++ {
        go func() {
            for {
                atomic.AddUint64(&amp;ops, 1)

                runtime.Gosched()
            }
        }()
    }

    time.Sleep(time.Second)

    opsFinal := atomic.LoadUint64(&amp;ops)
    fmt.Println(&quot;ops:&quot;, opsFinal)
}

without the runtime.Gosched() (go run conc.go) and the program never exited even when I reduced the loop from 50 to 1.

Question:

What happens under the hood after the call to atomic.AddUint64 that it is necessary to call runtime.Gosched? And how does runtime.Gosched fixes this? I did not find any hint to such a thing in sync/atomic's documentation.

答案1

得分: 5

这是合作式多线程的工作原理。如果一个线程保持就绪状态,它将继续运行,而其他线程则不会运行。使用显式和隐式的抢占点来允许其他线程运行。如果你的线程在一个循环中停留了很长时间而没有隐式的抢占点,如果你不添加一个显式的抢占点,你将会饿死其他线程。

这个答案提供了关于Go语言何时使用合作式多线程的更多信息。

英文:

This is how cooperative multithreading works. If one thread remains ready to run, it continues to run and other threads don't. Explicit and implicit pre-emption points are used to allow other threads to run. If your thread has a loop that it stays in for lots of time with no implicit pre-emption points, you'll starve other threads if you don't add an explicit pre-emption point.

This answer has much more information about when Go uses cooperative multithreading.

huangapple
  • 本文由 发表于 2015年10月19日 17:05:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/33210519.html
匿名

发表评论

匿名网友

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

确定