首頁 >後端開發 >Golang >等待映射中的值在 Go 中可用

等待映射中的值在 Go 中可用

王林
王林轉載
2024-02-05 21:51:071225瀏覽

等待映射中的值在 Go 中可用

問題內容

我有一個程序,基本上有三種情況- 設定鍵的值,取得值(如果存在),或等到給定鍵的值可用。我最初的想法 - 建立一個帶有 map[string]interface{} 的新類型 - 其中儲存「持久」值。除此之外,為了等待一個值,我計畫使用 map[string](chan struct{})。當呼叫 set() 方法時,我會寫入該通道,任何等待它的人都會知道該值在那裡。

我事先不知道密鑰 - 它們是隨機的。我不確定如何正確實作 wait() 方法。

type Map struct {
    sync.Mutex

    m    map[string]interface{}
    wait map[string]chan (struct{})
}


func (m *Map) Set(key string, value interface{}) {
    m.ensureWaitChan(key)

    m.Lock()
    defer m.Unlock()

    m.m[key] = value

    // Signal to all waiting.
    m.wait[key] <- struct{}{}
}


func (m *Map) Wait(key string) interface{} {
    m.ensureWaitChan(key)

    m.Lock()
    
    value, ok := m.m[key]
    if ok {
        m.Unlock()
        return value
    }

    m.Unlock()
    // <------ Unlocked state where something might happen.
    <-m.wait[key]

    value := m.m[key]

    return value    
}

// If the channel does not exist for those waiting - create it.
func (m *Map) ensureWaitChan(key string) {
    m.Lock()
    defer m.Unlock()

    _, ok := m.wait[key]
    if ok {
        return
    }

    m.wait[key] = make(chan struct{}, 100)
}

問題是 - wait() 中存​​在競爭條件 - 在我釋放互斥體之後,在我開始偵聽通道上的傳入值之前。

處理這個問題的最佳方法是什麼?歡迎任何其他關於如何實現這一點的建議,我相信一定有更好的方法來做到這一點。我不會以固定的時間間隔或類似的方式輪詢該值。


正確答案


您正在尋找的是同步映射和訊息代理之間的混合。我們可以透過利用通訊和同步通道來實現這一點,以便訂閱者可以在訊息發布後立即收到訊息(如果訊息尚未在快取中)。

type Map struct {
    sync.Mutex

    m    map[string]any
    subs map[string][]chan any
}

func (m *Map) Set(key string, value any) {
    m.Lock()
    defer m.Unlock()

    m.m[key] = value

    // Send the new value to all waiting subscribers of the key
    for _, sub := range m.subs[key] {
        sub <- value
    }
    delete(m.subs, key)
}

func (m *Map) Wait(key string) any {
    m.Lock()
    // Unlock cannot be deferred so we can unblock Set() while waiting

    value, ok := m.m[key]
    if ok {
        m.Unlock()
        return value
    }

    // if there is no value yet, subscribe to any new values for this key
    ch := make(chan any)
    m.subs[key] = append(m.subs[key], ch)
    m.Unlock()

    return <-ch
}

由於訂閱者在等待時必須解鎖地圖互斥體,因此他們無法安全地存取新增到地圖中的新訊息。我們透過自己的頻道將新值直接發送給所有訂閱者,這樣我們就不需要在 set 中添加更多同步,以確保所有訂閱者在解鎖地圖本身之前都滿意。提前解鎖地圖將允許訂閱者直接讀取它,但也會允許同時插入新值,從而導致結果不一致。

正在運行的版本,還包括具有類型參數的通用 map 實作: https://go.dev /play/p/an7vrspdgmo

#

以上是等待映射中的值在 Go 中可用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除