我有一個程序,基本上有三種情況- 設定鍵的值,取得值(如果存在),或等到給定鍵的值可用。我最初的想法 - 建立一個帶有 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中文網其他相關文章!