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

Ble*_*ers 3 multithreading atomic go

通过示例Go:原子计数器.代码示例在调用runtime.Gosched后调用atomic.AddUint64.

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)
}
Run Code Online (Sandbox Code Playgroud)

没有runtime.Gosched()(go run conc.go)和程序永远不会退出,即使我将循环从50减少到1.

题:

在呼叫之后发生什么事情atomic.AddUint64有必要打电话runtime.Gosched?这是怎么runtime.Gosched解决的?我在sync/atomic文档中没有找到任何暗示这样的东西.

Dav*_*rtz 5

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

这个答案提供了有关Go何时使用协作多线程的更多信息.

  • @wldsvc这不会改变任何东西.你仍然有合作多线程.例如,如果GOMAXPROCS是8并且你有9个线程,其中没有任何一个点击任何抢占点,其中一个线程仍然会被饿死.这不是关于你有多少线程,而是关于如何切换线程正在执行的任务.(此外,仍有机器只有一个核心.具有一个核心的虚拟机甚至不常见.) (6认同)