为什么我们需要在调用atomic.AddUint64和其他类似的原子操作后调用runtime.Gosched?

时间:2015-10-19 09:05:04

标签: multithreading go atomic

浏览 Go by Example: Atomic Counters 。代码示例在调用runtime.Gosched后调用atomic.AddUint64

atomic.AddUint64被调用

  

ensure that this goroutine doesn’t starve the scheduler

不幸的是,我发现解释并不那么内容和令人满意。

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

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.Gosched后,幕后发生了什么? runtime.Gosched如何解决这个问题?我在sync/atomic的文档中没有找到任何暗示。

1 个答案:

答案 0 :(得分:5)

这就是cooperative multithreading的工作原理。如果一个线程仍然准备好运行,它将继续运行而其他线程则不会运行。显式和隐式抢占点用于允许其他线程运行。如果你的线程有一个循环,它在没有隐式抢占点的情况下会保持很长时间,那么如果你没有添加明确的抢先点,你就会饿死其他线程。

This answer提供了有关Go何时使用合作多线程的更多信息。