亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

一段時間后停止 goroutine

一段時間后停止 goroutine

Go
江戶川亂折騰 2023-08-14 17:04:33
像這樣在一段時間后停止 goroutine 并發安全嗎?(注意:由于另一個 goroutine 發生變化而導致數據競爭):okpackage mainimport (? ? "fmt"? ? "time")func main() {? ? var ok byte? ? time.AfterFunc(1000*time.Millisecond, func() {? ? ? ? ok = 1? ? })? ? var i uint64? ? for ok == 0 {? ? ? ? i++ // CPU intensive task? ? }? ? fmt.Println(i) // 2_776_813_033}終端:go run -race .==================WARNING: DATA RACEWrite at 0x00c000132010 by goroutine 8:? main.main.func1()? ? ? ./main.go:11 +0x46Previous read at 0x00c000132010 by main goroutine:? main.main()? ? ? ./main.go:15 +0xf4Goroutine 8 (running) created at:? time.goFunc()? ? ? go/src/time/sleep.go:180 +0x51==================80849692Found 1 data race(s)代碼(無數據競爭):package mainimport (? ? "fmt"? ? "sync/atomic"? ? "time")func main() {? ? var ok int32? ? time.AfterFunc(1000*time.Millisecond, func() {? ? ? ? atomic.StoreInt32(&ok, 1)? ? })? ? var i uint64? ? for atomic.LoadInt32(&ok) == 0 {? ? ? ? i++ // CPU intensive task? ? }? ? fmt.Println(i) // 2_835_935_488}終端:go run -race .31934042
查看完整描述

2 回答

?
慕勒3428872

TA貢獻1848條經驗 獲得超6個贊

即使被另一個 goroutineok設置為busy-wait for 循環,也不能保證它會終止。false在設置和讀取 的過程中沒有顯式同步ok,因此不能保證主 Goroutine 能夠看到對其所做的更改。換句話說,沒有辦法在兩個 goroutine 之間建立發生之前的關系。

https://golang.org/ref/mem

代碼的第二個版本是安全的,盡管 Go 內存模型中沒有針對 進行說明ok,但它并不安全,因為如此緊密的循環可能不允許其他 goroutine 執行。原子讀/寫具有發生之前關系所需的內存屏障。您應該使用同步原語之一(互斥體、通道)來保證這一點。


查看完整回答
反對 回復 2023-08-14
?
繁花不似錦

TA貢獻1851條經驗 獲得超4個贊

第二個代碼適用于 Go 1.14+:


去1.14


Goroutines 現在是異步可搶占的。因此,沒有函數調用的循環不再可能導致調度程序死鎖或顯著延遲垃圾收集。除了 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


搶占是調度程序的重要組成部分,它可以在 goroutine 之間分配運行時間。事實上,如果沒有搶占,一個長時間運行的 goroutine 會占用 CPU,從而阻止其他 goroutine 被調度。1.14版本引入了異步搶占的新技術,為調度程序提供了更多的權力和控制權。


查看完整回答
反對 回復 2023-08-14
  • 2 回答
  • 0 關注
  • 160 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號