我有一個地圖清除地圖之前需要釋放的對象。當我走過它時,我試圖迭代地圖並移除/釋放對象。Golang併發地圖訪問與範圍
這是一個模擬了例如 https://play.golang.org/p/kAtPoUgMsq
由於遍歷地圖的唯一方法是通過範圍,我將如何同步多個生產者和多個消費者?
我不想讀取鎖定地圖,因爲這將使迭代過程中刪除/修改鍵不可能。
我有一個地圖清除地圖之前需要釋放的對象。當我走過它時,我試圖迭代地圖並移除/釋放對象。Golang併發地圖訪問與範圍
這是一個模擬了例如 https://play.golang.org/p/kAtPoUgMsq
由於遍歷地圖的唯一方法是通過範圍,我將如何同步多個生產者和多個消費者?
我不想讀取鎖定地圖,因爲這將使迭代過程中刪除/修改鍵不可能。
你沒有說明所有的要求(例如可以同時發佈多個對象等),但我能想到的最簡單的解決方案是刪除元素併爲每個刪除的元素啓動一個發佈goroutine:
for key := range keysToRemove {
if v, ok := m[k]; ok {
delete(m, k)
go release(k, v)
}
}
更新2017年8月(golang 1.9)
您現在在sync
包的新Map
類型與攤銷的固定時間的加載,存儲併發地圖,並刪除。
多個goroutines同時調用Map的方法是安全的。
原來的答覆2016年11月
我不想讀鎖地圖
這是有道理的,因爲從地圖刪除被認爲是一個寫操作,並且必須與所有其他讀寫操作串行化。這意味着一個寫鎖定來完成刪除。 (來源:this answer)
假設最壞的情況(多作家和讀者),你可以看看的orcaman/concurrent-map
的實現,它使用多個sync.RWMutex
因爲,避免鎖瓶頸Remove()
method,這同時地圖潛入數個(SHARD_COUNT
)地圖碎片。
這比僅使用一個RWMutex
as in this example快。
有很多方法可以清除map
中的內容,而不會激活地圖訪問。什麼適用於你的應用程序取決於它在做什麼。
0)只要在工作時鎖定地圖。如果地圖不是太大,或者您有一定的延遲容忍度,那麼它可以快速完成工作(根據時間您在其上花費),並且您可以繼續思考其他內容。如果它稍後成爲問題,那麼您可以回到問題。
1)複製對象或指針並清除地圖,同時按住鎖定,然後釋放背景中的對象。如果問題在於緩慢釋放本身會使鎖持續很長時間,這是一個簡單的解決方法。
2)如果高效讀取基本上都是重要的,請使用atomic.Value
。這可以讓你完全用一個新的和不同的地圖替換一個地圖。如果寫入基本上佔工作負載的0%,則高效讀取將平衡每次更改時創建新映射的成本。這很少見,但它發生,例如,encoding/gob
有一個這種方式管理的類型的全球地圖。
3)如果沒有那些你需要的一切,調整你如何存儲數據(例如,分片地圖)。用你自己的16個地圖和散列鍵替換你的地圖來決定一個東西屬於哪個地圖,然後你可以一次鎖定一個碎片,進行清理或其他寫入。
還有釋放和使用之間的種族問題:夠程一個從地圖得到的東西,B清除地圖和釋放的東西,A使用公佈的事情。
一種策略是在您使用或釋放它時鎖定每個值;那麼你需要鎖而不是全局鎖。
另一個是容忍種族的後果,如果他們知道,而不是災難性的;例如,其文檔明確允許對net.Conn
的併發訪問,因此關閉正在使用的連接可能會導致對其的錯誤請求,但不會導致未定義的應用程序行爲。但是,你必須確定你知道你進入的是什麼,因爲many benign-seeming races aren't。
最後,也許你的應用程序已經確保沒有正在使用的對象被釋放,例如,對象上有一個安全維護的引用計數,只有未使用的對象被釋放。那麼,當然,你不必擔心。
這可能是很有誘惑力的嘗試以某種方式取代這些鎖與渠道,但我沒有看到任何收益。 不錯,當你可以設計你的應用程序時主要考慮進程之間的通信而不是共享數據,但是當你有共享數據時,沒有用的假裝別的方法。排除對共享數據的不安全訪問是鎖定的目的。
我們需要更多的上下文來處理這個問題。地圖包含什麼?它巨大嗎?釋放一件東西有多快? (什麼_is_釋放?)如果一個釋放的對象被另一個goroutine使用,它會是一場災難嗎?如果是這樣,確定從地圖中拾取的對象仍在使用多長時間?什麼是應用程序,它的優先級是什麼(例如,最大化吞吐量與限制延遲)? – twotwotwo