第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

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

在讀取和修改之前鎖定切片

在讀取和修改之前鎖定切片

Go
慕姐8265434 2023-02-21 12:40:26
我使用 Go 的經(jīng)驗是最近的,在審查一些代碼時,我看到雖然它被寫保護,但讀取數(shù)據(jù)時存在問題。不是閱讀本身,而是閱讀和修改切片之間可能發(fā)生的修改。type ConcurrentSlice struct {    sync.RWMutex    items []Item}type Item struct {    Index int    Value Info}type Info struct {    Name        string     Labels      map[string]string    Failure     bool}如前所述,寫作以這種方式受到保護:func (cs *ConcurrentSlice) UpdateOrAppend(item ScalingInfo) {    found := false    i := 0    for inList := range cs.Iter() {        if item.Name == inList.Value.Name{            cs.items[i] = item            found = true        }        i++    }    if !found {        cs.Lock()        defer cs.Unlock()        cs.items = append(cs.items, item)    }}func (cs *ConcurrentSlice) Iter() <-chan ConcurrentSliceItem {    c := make(chan ConcurrentSliceItem)    f := func() {        cs.Lock()        defer cs.Unlock()        for index, value := range cs.items {            c <- ConcurrentSliceItem{index, value}        }        close(c)    }    go f()    return c}但是在收集切片的內(nèi)容和修改它之間,可能會發(fā)生修改??赡苁橇硪粋€例程修改了同一個切片,當(dāng)該賦值時,它已經(jīng)不存在了:slice[i] = item處理這個問題的正確方法是什么?我已經(jīng)實現(xiàn)了這個方法:func GetList() *ConcurrentSlice {    if list == nil {        denylist = NewConcurrentSlice()        return denylist    }    return denylist}我這樣使用它:concurrentSlice := GetList()concurrentSlice.UpdateOrAppend(item)但我知道在獲取和修改之間,即使它實際上是立即的,另一個例程也可能修改了切片。以原子方式執(zhí)行這兩個操作的正確方法是什么?我閱讀的切片 100% 是我修改的切片。因為如果我試圖將一個項目分配給一個不再存在的索引,它會中斷執(zhí)行。先感謝您!
查看完整描述

5 回答

?
幕布斯6054654

TA貢獻(xiàn)1876條經(jīng)驗 獲得超7個贊

您進(jìn)行阻止的方式是不正確的,因為它不能確保您退回的物品沒有被移除。在更新的情況下,數(shù)組仍將至少保持相同的長度。


一個更簡單的可行解決方案如下:


func (cs *ConcurrentSlice) UpdateOrAppend(item ScalingInfo) {

    found := false

    i := 0

    cs.Lock()

    defer cs.Unlock()


    for _, it := range cs.items {

        if item.Name == it.Name{

            cs.items[i] = it

            found = true

        }

        i++

    }

    if !found {

        cs.items = append(cs.items, item)

    }

}


查看完整回答
反對 回復(fù) 2023-02-21
?
翻閱古今

TA貢獻(xiàn)1780條經(jīng)驗 獲得超5個贊

如果值的順序不重要,請使用sync.Map 。

type Items struct {

    m sync.Map

}


func (items *Items) Update(item Info) {

    items.m.Store(item.Name, item)

}


func (items *Items) Range(f func(Info) bool) {

    items.m.Range(func(key, value any) bool {

        return f(value.(Info))

    })

}


查看完整回答
反對 回復(fù) 2023-02-21
?
慕哥6287543

TA貢獻(xiàn)1831條經(jīng)驗 獲得超10個贊

數(shù)據(jù)結(jié)構(gòu) 101:始終為您的用例選擇最佳數(shù)據(jù)結(jié)構(gòu)。如果您要按名稱查找對象,那正是 map 的用途。如果您仍然需要維護項目的順序,則使用樹圖


并發(fā) 101:與事務(wù)一樣,您的互斥量應(yīng)該是原子的、一致的和隔離的。你在這里隔離失敗,因為讀取的數(shù)據(jù)結(jié)構(gòu)不在你的互斥鎖內(nèi)。


您的代碼應(yīng)如下所示:


func {

 mutex.lock

 defer mutex.unlock

 check map or treemap for name

 if exists update

 else add

}


查看完整回答
反對 回復(fù) 2023-02-21
?
拉莫斯之舞

TA貢獻(xiàn)1820條經(jīng)驗 獲得超10個贊

經(jīng)過一些測試,我可以說你擔(dān)心的情況確實會發(fā)生sync.RWMutex。我認(rèn)為它也可能發(fā)生sync.Mutex,但我無法重現(xiàn)。也許我遺漏了一些信息,或者調(diào)用是有序的,因為它們都被阻止了,并且它們贖回鎖定權(quán)的順序是以某種方式排序的。

在沒有其他例程進(jìn)入“沖突”的情況下保持兩個調(diào)用安全的一種方法是為該對象上的每個任務(wù)使用另一個互斥鎖。您將在讀寫之前鎖定該互斥鎖,并在完成后釋放它。您還必須在寫入(或讀?。┰搶ο蟮娜魏纹渌{(diào)用上使用該互斥鎖。您可以在 main.go 文件中找到我所說內(nèi)容的實現(xiàn)。為了重現(xiàn) RWMutex 的問題,您可以簡單地注釋 startTask 和 endTask 調(diào)用,并且該問題在終端輸出中可見。

編輯:我的第一個答案是錯誤的,因為我誤解了測試結(jié)果,并陷入了 OP 描述的情況。


查看完整回答
反對 回復(fù) 2023-02-21
?
慕娘9325324

TA貢獻(xiàn)1783條經(jīng)驗 獲得超4個贊

如果ConcurrentSlice要從單個 goroutine 使用,則不需要鎖,因為在那里編寫的算法不會對 slice 元素或 slice 進(jìn)行任何并發(fā)讀/寫。


如果ConcurrentSlice要從多個 goroutines 使用,現(xiàn)有的鎖是不夠的。這是因為UpdateOrAppend可能同時修改切片元素。


安全版本需要兩個版本Iter:


這可以由 的用戶調(diào)用ConcurrentSlice,但不能從 `UpdateOrAppend 中調(diào)用:


func (cs *ConcurrentSlice) Iter() <-chan ConcurrentSliceItem {

    c := make(chan ConcurrentSliceItem)


    f := func() {

        cs.RLock()

        defer cs.RUnlock()

        for index, value := range cs.items {

            c <- ConcurrentSliceItem{index, value}

        }

        close(c)

    }

    go f()


    return c

}

這只能從以下位置調(diào)用UpdateOrAppend:


func (cs *ConcurrentSlice) internalIter() <-chan ConcurrentSliceItem {

    c := make(chan ConcurrentSliceItem)


    f := func() {

        // No locking

        for index, value := range cs.items {

            c <- ConcurrentSliceItem{index, value}

        }

        close(c)

    }

    go f()


    return c

}

并且UpdateOrAppend應(yīng)該在頂層同步:


func (cs *ConcurrentSlice) UpdateOrAppend(item ScalingInfo) {

cs.Lock()

defer cs.Unlock()

....

}

這是長版:


這是一段有趣的代碼。Iter()根據(jù)我對 go 內(nèi)存模型的理解,只有當(dāng)有另一個 goroutine 在處理這段代碼時才需要互斥鎖,即使這樣,代碼中也可能存在競爭。但是,UpdateOrAppend只修改索引低于Iter正在處理的索引的切片元素,因此競爭永遠(yuǎn)不會表現(xiàn)出來。

比賽可以按如下方式進(jìn)行:

  1. iter 中的 for 循環(huán)讀取切片的元素 0

  2. 元素通過通道發(fā)送。因此,切片接收發(fā)生在第一步之后。

  3. 接收端可能會更新切片的元素 0。到這里沒有問題。

  4. 然后發(fā)送 goroutine 讀取切片的元素 1。這是比賽可以發(fā)生的時候。如果第 3 步更新了切片的索引 1,則第 4 步的讀取是一場競賽。即:如果第 3 步讀取到第 4 步完成的更新,則這是一場比賽。如果您在 UpdateOrAppend 中以 i:=1 開始,并使用 -race 標(biāo)志運行它,您可以看到這一點。

但是UpdateOrAppend總是修改 i=0 時已經(jīng)看到的切片元素Iter,所以這段代碼是安全的,即使沒有鎖。

如果有其他 goroutines 訪問和修改結(jié)構(gòu),你需要 Mutex,但你需要它來保護完整的UpdateOrAppend方法,因為應(yīng)該只允許一個 goroutine 運行它。您需要互斥鎖來保護第一個 for 循環(huán)中的潛在更新,并且該互斥鎖還必須包括切片追加情況,因為這實際上可能會修改底層對象的切片。

如果Iter僅調(diào)用 from UpdateOrAppend,那么這個單個互斥鎖就足夠了。但是,如果Iter可以從多個 goroutines 調(diào)用,那么還有另一種競爭可能性。如果一個實例UpdateOrAppend與多個實例并發(fā)運行Iter,那么其中一些Iter實例將同時從修改后的切片元素中讀取,從而導(dǎo)致競爭。所以,應(yīng)該是 multiple Iters 只有在沒有調(diào)用的情況下才能運行UpdateOrAppend。那是一個 RWMutex。

但是可以從帶鎖的地方Iter調(diào)用,所以不能真的調(diào)用,否則就是死鎖。UpdateOrAppendRLock

因此,您需要兩個版本Iter:一個可以在外部調(diào)用UpdateOrAppend,在 goroutine 中發(fā)出RLock,另一個只能從中調(diào)用UpdateOrAppend,不能調(diào)用RLock。


查看完整回答
反對 回復(fù) 2023-02-21
  • 5 回答
  • 0 關(guān)注
  • 188 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號