为什么 goroutine 无法读取全局 var ops 值?
Why goroutine cannot read the global var ops value?
package main
import "fmt"
import "time"
import (
"runtime"
"sync/atomic"
)
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
func main() {
var t1 = time.Now()
var ops uint64 = 0
go func() {
for {
time.Sleep(time.Second)
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal, "qps:", opsFinal/uint64(time.Since(t1).Seconds()))
}
}()
for {
atomic.AddUint64(&ops, 1)
//runtime.Gosched()
}
}
在这种情况下每秒输出 "ops: 0 qps: 0",为什么不能在 goroutine 中读取操作?
但是当添加runtime.Gosched()时,一切正常!
大家能帮帮我吗?
我更新了go版本,请检查,
[mh-cbon@pc3 y] $ go run main.go
ops: 97465383 qps: 97465383
ops: 195722110 qps: 97861055
ops: 293058057 qps: 97686019
ops: 390971243 qps: 97742810
^Csignal: interrupt
[mh-cbon@pc3 y] $ go version
go version go1.10 linux/amd64
[mh-cbon@pc3 y] $ gvm use 1.8
Now using version go1.8
[mh-cbon@pc3 y] $ go version
go version go1.8 linux/amd64
[mh-cbon@pc3 y] $ go run main.go
ops: 0 qps: 0
ops: 0 qps: 0
^Csignal: interrupt
[mh-cbon@pc3 y] $
我对 The Go Memory Model 的解读是,这是对您编写的程序的正确执行:无法保证主程序中的 AddUint64()
调用 发生在 goroutine 中的 LoadUint64()
调用,因此每次读取变量都发生在任何写入发生之前是合法的。如果编译器知道 "sync/atomic"
是特殊的并得出增量的结果不可观察的结论,那么我不会完全震惊,所以只删除了最后一个循环。
The Go Memory Model 和 sync/atomic 文档 都反对您正在使用的方法。 "sync/atomic"
告诫:
Share memory by communicating; don't communicate by sharing memory.
更好的程序可能如下所示:
package main
import "fmt"
import "time"
func count(op <-chan struct{}) {
t1 := time.Now()
ops := 0
tick := time.Tick(time.Second)
for {
select {
case <-op:
ops++
case <-tick:
dt := time.Since(t1).Seconds()
fmt.Printf("ops: %d qps: %f\n", ops, float64(ops)/dt)
}
}
}
func main() {
op := make(chan struct{})
go count(op)
for {
op <- struct{}{}
}
}
请注意,除了通过通道发送的数据外,主程序和 goroutine 之间不共享任何状态。
package main
import "fmt"
import "time"
import (
"runtime"
"sync/atomic"
)
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
}
func main() {
var t1 = time.Now()
var ops uint64 = 0
go func() {
for {
time.Sleep(time.Second)
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal, "qps:", opsFinal/uint64(time.Since(t1).Seconds()))
}
}()
for {
atomic.AddUint64(&ops, 1)
//runtime.Gosched()
}
}
在这种情况下每秒输出 "ops: 0 qps: 0",为什么不能在 goroutine 中读取操作?
但是当添加runtime.Gosched()时,一切正常!
大家能帮帮我吗?
我更新了go版本,请检查,
[mh-cbon@pc3 y] $ go run main.go
ops: 97465383 qps: 97465383
ops: 195722110 qps: 97861055
ops: 293058057 qps: 97686019
ops: 390971243 qps: 97742810
^Csignal: interrupt
[mh-cbon@pc3 y] $ go version
go version go1.10 linux/amd64
[mh-cbon@pc3 y] $ gvm use 1.8
Now using version go1.8
[mh-cbon@pc3 y] $ go version
go version go1.8 linux/amd64
[mh-cbon@pc3 y] $ go run main.go
ops: 0 qps: 0
ops: 0 qps: 0
^Csignal: interrupt
[mh-cbon@pc3 y] $
我对 The Go Memory Model 的解读是,这是对您编写的程序的正确执行:无法保证主程序中的 AddUint64()
调用 发生在 goroutine 中的 LoadUint64()
调用,因此每次读取变量都发生在任何写入发生之前是合法的。如果编译器知道 "sync/atomic"
是特殊的并得出增量的结果不可观察的结论,那么我不会完全震惊,所以只删除了最后一个循环。
The Go Memory Model 和 sync/atomic 文档 都反对您正在使用的方法。 "sync/atomic"
告诫:
Share memory by communicating; don't communicate by sharing memory.
更好的程序可能如下所示:
package main
import "fmt"
import "time"
func count(op <-chan struct{}) {
t1 := time.Now()
ops := 0
tick := time.Tick(time.Second)
for {
select {
case <-op:
ops++
case <-tick:
dt := time.Since(t1).Seconds()
fmt.Printf("ops: %d qps: %f\n", ops, float64(ops)/dt)
}
}
}
func main() {
op := make(chan struct{})
go count(op)
for {
op <- struct{}{}
}
}
请注意,除了通过通道发送的数据外,主程序和 goroutine 之间不共享任何状态。