浏览 Go by Example: Atomic Counters 。代码示例在调用runtime.Gosched
后调用atomic.AddUint64
。
atomic.AddUint64
被调用
不幸的是,我发现解释并不那么内容和令人满意。
我尝试运行示例代码(为了简洁而删除了注释):
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
的文档中没有找到任何暗示。
答案 0 :(得分:5)
这就是cooperative multithreading的工作原理。如果一个线程仍然准备好运行,它将继续运行而其他线程则不会运行。显式和隐式抢占点用于允许其他线程运行。如果你的线程有一个循环,它在没有隐式抢占点的情况下会保持很长时间,那么如果你没有添加明确的抢先点,你就会饿死其他线程。
This answer提供了有关Go何时使用合作多线程的更多信息。