2 回答
TA贡献1853条经验 获得超6个赞
即使被另一个 goroutineok
设置为,也不能保证忙等待循环将终止。false
在设置和读取 的过程中没有显式同步ok
,因此不能保证主 goroutine 看到对其所做的更改。换句话说,没有办法在两个 goroutine 之间建立发生前的关系。
代码的第二个版本是安全的,即使在 Go 内存模型中ok
没有关于 . 原子读/写具有发生之前关系所必需的内存屏障。您应该使用同步原语之一(互斥体、通道)来保证这一点。
TA贡献1772条经验 获得超5个赞
Go 内存模型:
建议
修改多个 goroutine 同时访问的数据的程序必须序列化这种访问。
要序列化访问,请使用通道操作或其他同步原语(例如同步和同步/原子包中的同步原语)保护数据。
对于第一个代码,您应该使用适当的同步,例如:
“上下文”、“同步/原子” sync.Mutex、或通道。
去1.14
Goroutines 现在是异步可抢占的。因此,没有函数调用的循环不再可能使调度程序死锁或显着延迟垃圾收集。这在除 windows/arm、darwin/arm、js/wasm 和 plan9/* 之外的所有平台上都受支持。
一段时间后停止 gouroutine
BenchmarkAfterFunc-8 1000000000 0.4468 ns/op 0 B/op 0 allocs/op
BenchmarkDoneChannel-8 121966824 9.855 ns/op 0 B/op 0 allocs/op
BenchmarkTimeSince-8 89790115 12.95 ns/op 0 B/op 0 allocs/op
BenchmarkContextErr-8 58508900 19.78 ns/op 0 B/op 0 allocs/op
BenchmarkAfterFuncMutex-8 58323207 20.00 ns/op 0 B/op 0 allocs/op
BenchmarkContext-8 48947625 27.43 ns/op 0 B/op 0 allocs/op
测试:
package main
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
)
const d = 200 * time.Millisecond // To stop a task after a period of time
func BenchmarkTimeSince(b *testing.B) {
t0 := time.Now()
var count = 0
for i := 0; i < b.N; i++ {
if time.Since(t0) < d {
count++
}
}
_ = count
}
func BenchmarkContext(b *testing.B) {
var ctx, cancel = context.WithTimeout(context.Background(), d)
defer cancel()
var count = 0
for i := 0; i < b.N; i++ {
select {
case <-ctx.Done():
// break
default:
count++
}
}
_ = count
}
func BenchmarkContextErr(b *testing.B) {
var ctx, cancel = context.WithTimeout(context.Background(), d)
defer cancel()
var count = 0
for i := 0; i < b.N; i++ {
if ctx.Err() == nil {
count++
}
}
_ = count
}
func BenchmarkAfterFunc(b *testing.B) {
var done uint32
time.AfterFunc(d, func() { atomic.StoreUint32(&done, 1) })
var count = 0
for i := 0; i < b.N; i++ {
if atomic.LoadUint32(&done) == 0 {
count++
}
}
_ = count
}
func BenchmarkDoneChannel(b *testing.B) {
var done = make(chan struct{})
time.AfterFunc(d, func() { close(done) })
var count = 0
for i := 0; i < b.N; i++ {
select {
case <-done:
// break
default:
count++
}
}
_ = count
}
type foo struct {
sync.Mutex
state bool
}
func (p *foo) end() {
p.Lock()
p.state = true
p.Unlock()
}
func (p *foo) isDone() bool {
var b bool
p.Lock()
b = p.state
p.Unlock()
return b
}
func BenchmarkAfterFuncMutex(b *testing.B) {
var it = foo{}
time.AfterFunc(d, func() { it.end() })
var count = 0
for i := 0; i < b.N; i++ {
if it.isDone() {
count++
}
}
_ = count
}
https://medium.com/a-journey-with-go/go-asynchronous-preemption-b5194227371c
抢占是调度器的重要组成部分,它可以在 goroutine 之间分配运行时间。事实上,如果没有抢占,一个长时间运行的占用 CPU 的 goroutine 会阻止其他 goroutine 被调度。1.14 版本引入了一种异步抢占的新技术,为调度程序提供了更多的权力和控制权。
- 2 回答
- 0 关注
- 158 浏览
添加回答
举报