2015-12-04 56 views
13

我有一個地圖清除地圖之前需要釋放的對象。當我走過它時,我試圖迭代地圖並移除/釋放對象。Golang併發地圖訪問與範圍

這是一個模擬了例如 https://play.golang.org/p/kAtPoUgMsq

由於遍歷地圖的唯一方法是通過範圍,我將如何同步多個生產者和多個消費者?

我不想讀取鎖定地圖,因爲這將使迭代過程中刪除/修改鍵不可能。

+0

我們需要更多的上下文來處理這個問題。地圖包含什麼?它巨大嗎?釋放一件東西有多快? (什麼_is_釋放?)如果一個釋放的對象被另一個goroutine使用,它會是一場災難嗎?如果是這樣,確定從地圖中拾取的對象仍在使用多長時間?什麼是應用程序,它的優先級是什麼(例如,最大化吞吐量與限制延遲)? – twotwotwo

回答

4

你沒有說明所有的要求(例如可以同時發佈多個對象等),但我能想到的最簡單的解決方案是刪除元素併爲每個刪除的元素啓動一個發佈goroutine:

for key := range keysToRemove { 
    if v, ok := m[k]; ok { 
     delete(m, k) 
     go release(k, v) 
    } 
} 
+0

在這個答案中給出的方法是否足夠? (特別是在1.6更改之後?)語言規範中是否有一個地方可以清除/明確地回答這個問題? – BadZen

+0

在上面的代碼示例中,map是從一個goroutine訪問的,所以不需要同步,因爲沒有併發訪問。 – kostya

+0

當然,OP假設在迭代過程中會有併發的作者。他說:「我不想讀鎖定地圖,因爲這會在迭代過程中刪除/修改密鑰變得不可能。」僅僅因爲他沒有/顯示/編劇並不意味着他們不參與。 – BadZen

2

更新2017年8月(golang 1.9

您現在在sync包的新Map類型與攤銷的固定時間的加載,存儲併發地圖,並刪除。
多個goroutines同時調用Map的方法是安全的。


原來的答覆2016年11月

我不想讀鎖地圖

這是有道理的,因爲從地圖刪除被認爲是一個寫操作,並且必須與所有其他讀寫操作串行化。這意味着一個寫鎖定來完成刪除。 (來源:this answer

假設最壞的情況(多作家和讀者),你可以看看的orcaman/concurrent-map的實現,它使用多個sync.RWMutex因爲,避免鎖瓶頸Remove() method,這同時地圖潛入數個(SHARD_COUNT)地圖碎片。
這比僅使用一個RWMutexas in this example快。

6

有很多方法可以清除map中的內容,而不會激活地圖訪問。什麼適用於你的應用程序取決於它在做什麼。

0)只要在工作時鎖定地圖。如果地圖不是太大,或者您有一定的延遲容忍度,那麼它可以快速完成工作(根據時間您在其上花費),並且您可以繼續思考其他內容。如果它稍後成爲問題,那麼您可以回到問題。

1)複製對象或指針並清除地圖,同時按住鎖定,然後釋放背景中的對象。如果問題在於緩慢釋放本身會使鎖持續很長時間,這是一個簡單的解決方法。

2)如果高效讀取基本上都是重要的,請使用atomic.Value。這可以讓你完全用一個新的和不同的地圖替換一個地圖。如果寫入基本上佔工作負載的0%,則高效讀取將平衡每次更改時創建新映射的成本。這很少見,但它發生,例如,encoding/gob有一個這種方式管理的類型的全球地圖。

3)如果沒有那些你需要的一切,調整你如何存儲數據(例如,分片地圖)。用你自己的16個地圖和散列鍵替換你的地圖來決定一個東西屬於哪個地圖,然後你可以一次鎖定一個碎片,進行清理或其他寫入。

還有釋放和使用之間的種族問題:夠程一個從地圖得到的東西,B清除地圖和釋放的東西,A使用公佈的事情。

一種策略是在您使用或釋放​​它時鎖定每個值;那麼你需要鎖而不是全局鎖。

另一個是容忍種族的後果,如果他們知道,而不是災難性的;例如,其文檔明確允許對net.Conn的併發訪問,因此關閉正在使用的連接可能會導致對其的錯誤請求,但不會導致未定義的應用程序行爲。但是,你必須確定你知道你進入的是什麼,因爲many benign-seeming races aren't

最後,也許你的應用程序已經確保沒有正在使用的對象被釋放,例如,對象上有一個安全維護的引用計數,只有未使用的對象被釋放。那麼,當然,你不必擔心。

這可能是很有誘惑力的嘗試以某種方式取代這些鎖與渠道,但我沒有看到任何收益。 不錯,當你可以設計你的應用程序時主要考慮進程之間的通信而不是共享數據,但是當你有共享數據時,沒有用的假裝別的方法。排除對共享數據的不安全訪問是鎖定的目的。