2 回答

TA貢獻1848條經(jīng)驗 獲得超6個贊
即使被另一個 goroutineok
設(shè)置為busy-wait for 循環(huán),也不能保證它會終止。false
在設(shè)置和讀取 的過程中沒有顯式同步ok
,因此不能保證主 Goroutine 能夠看到對其所做的更改。換句話說,沒有辦法在兩個 goroutine 之間建立發(fā)生之前的關(guān)系。
代碼的第二個版本是安全的,盡管 Go 內(nèi)存模型中沒有針對 進行說明ok
,但它并不安全,因為如此緊密的循環(huán)可能不允許其他 goroutine 執(zhí)行。原子讀/寫具有發(fā)生之前關(guān)系所需的內(nèi)存屏障。您應(yīng)該使用同步原語之一(互斥體、通道)來保證這一點。

TA貢獻1851條經(jīng)驗 獲得超4個贊
第二個代碼適用于 Go 1.14+:
去1.14
Goroutines 現(xiàn)在是異步可搶占的。因此,沒有函數(shù)調(diào)用的循環(huán)不再可能導致調(diào)度程序死鎖或顯著延遲垃圾收集。除了 windows/arm、darwin/arm、js/wasm 和 plan9/* 之外,所有平臺都支持此功能。
一段時間后停止 goroutine
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
搶占是調(diào)度程序的重要組成部分,它可以在 goroutine 之間分配運行時間。事實上,如果沒有搶占,一個長時間運行的 goroutine 會占用 CPU,從而阻止其他 goroutine 被調(diào)度。1.14版本引入了異步搶占的新技術(shù),為調(diào)度程序提供了更多的權(quán)力和控制權(quán)。
- 2 回答
- 0 關(guān)注
- 171 瀏覽
添加回答
舉報