多映射通用集合類(C#實現)–支援一鍵多值儲存

來源:互聯網
上載者:User

.net的通用Dictionary集合類有一個“鍵”唯一約束。考慮這樣一種情況:你想在Dictionary中存Author Name以及Articles。首先,你想加入Bob->Article_Good_One,而當你想加入Bob->Article_Good_Second,你將得到一個異常。這是因為Dictionary的唯一鍵約束。Dictionary拒絕接受相同的key,因為它要求鍵唯一。

Dictionary類被設計成對搜尋具有很高的效能。而多映射類在你想讓搜尋具有很高的效能以及讓它可以為一個相同的鍵增加多個值的時候可以使用。

背景

Dictionary通用集合是一個很好的資料結構。很多的一種情況,在程式開發或者開發服務的時候,我們需要為一個通常的Key儲存多個value。一個簡單的例子就是一個人有多個電話號碼,而存取層級需要基於人。

使用Dictionary時,代碼大致如下:

現在讓我們來看一下,使用多映射類集合類,怎樣來實現這個簡單的例子:

第一行建立了一個MultiMapBK對象。接下來的四行加入了四個人以及他們的電話號碼。然後,我們需要為這四個人設定其他的值。接下來的一些行用來檢索“Mai”以及列印所有的值。

多映射集合類的說明

下面的圖片解釋了MultiMapBK的內部資料結構。

就像上面的圖片顯示的,每一個值都被儲存在一個List對象中。而List能夠為一個key儲存超過一個value。

而這個多映射集合類也是型別安全和安全執行緒的。它也支援當一個線程在修改集合時,另一個線程可以枚舉它。

什麼是一個多映射集合

它最基本的功能是為一個鍵儲存多個值,然而同時,它也是一個“並發”集合。下面的圖片給出了一個多映射能夠實現的功能。

使用代碼

使用這個集合類需要下面的幾個步驟:

1)  在VS2005或者2008的解決方案中,右擊“引用”

2)  點擊“添加引用”同時加入下載下來的MultiMap.dll【附於文章結束的連結中,自行下載】

現在,讓我們來看一下,如何使用這段代碼

  1、  怎樣定義以及為該集合加入元素:

上面的第一行建立了一個新的集合對象。2,3,4行加入了幾個元素。注意,鍵“Alice”在所有的三個記錄中是相同的。

  2、  怎樣從集合中讀取元素

1,2,3,4行建立以及執行個體化了一個集合,第五行獲得了一個列舉程式來枚舉集合中的每一個元素。第七行,使用了MoveNext()來讀取一個特殊鍵(Alice)的每一個值。

  3、 怎樣來快速定位一個特別的項並列印出其所有的值

多映射集合的設計

    內部使用的資料結構

基本的內部結構如:

如上面所說的,Dictionary<Key, List<Value>>是用來為一個相同的鍵儲存多個值得資料結構。

線程訪問情境一

和通常的設計一樣,為了安全執行緒,集合類在增加,修改,以及刪除的時候是被鎖住的。它產生了一個很小的(通常是毫秒層級的)開銷。但是為這個類的使用者提供了巨大的靈活性。這樣這個集合類的使用者就不需要再擔心安全執行緒,同步等問題。

考慮這樣一種情況:線程1正在讀取集合中第五鍵的值。現在,線程2將該值刪除。那麼線程一的下一次讀取應該返回null。鎖住內部的資料結構可以完成這個功能。

線程訪問情境二

下一個情境是線程感知。考慮這樣一種情況,兩個不同的線程都正在枚舉同一個鍵,該鍵有100個值,正如下面的圖片顯示的:

現在,當線程2被執行個體化來讀取Key_5的值的時候,線程1已經讀取了該鍵的5個值。而現在,下一次對MultiMap's GetCurrentItem方法的調用,需要知道返回哪一個值。例如,是對線程1來講的第六個值還是對線程2來講的第一個值。為了應對這樣的情況,MultiMap儲存了線程的明細在一個獨立的集合中。它檢索一個值來查看哪個值是被哪個線程最後一次讀取的。使用線程的明細,MultiMap將能夠為正確的線程儲存正確的值。而用多線程執行下面的代碼,也仍然是正確的:

線程訪問情境三

下一步是完成在枚舉時的安全執行緒。

考慮如下的情境,線程一使用MoveNext()來擷取Current屬性。與此同時,線程2刪除了被線程1“命中”的當前項。如果這個情境不被處理,我們將會線上程1上看到一個有趣的“NullReferenceException”異常。一個基本的想法就是:當線程1調用MoveNext()方法的時候,讓它擁有該Current屬性。然而,當線程2企圖刪除線程1的Current時,有兩個選擇提供給線程2。選擇1:線程2可以阻塞“刪除”,直到線程一調用MoveNext方法或者移動到下一項時,刪除調用才返回。選擇2:線程2可以採用不阻塞地調用刪除方法,並且調用可以立即返回。但該項將被標識為“刪除項”,但此時仍然存在。當線程1從該被標識的項上移走時,該項將會被安全地從集合中刪除。

為了簡便起見,讓我們離開這些並發情境。然而,主要的並發問題被列舉在下一節中。

解決並發問題

討論所有的並發問題的處理,將會使這篇文章變得非常長。然而,列舉出那些需要被處理的並發問題,卻是很有用的:

1)  多線程更新(或者插入),在同一個MultiMap集合的執行個體上。項以它們接受到的順序添加。

2)  在不同的線程之間枚舉。例如,一個線程建立了一個列舉程式,但是被傳遞給另一個線程使用?允許,甚至允許在更多的線程之間共用同一個列舉程式的執行個體,每一個線程將獲得它自己正確的“next_item”(在方法MoveNext上)和Current。

3)  一個線程從集合中刪除一項,而此時另一個線程正在枚舉或者讀取相同的項?允許。一旦線程通過讀取元素的MoveNext方法,或thread_abort或thread_close離開該元素,那麼刪除將生效。然而,有趣的是,當刪除這個項的線程再次枚舉該刪除的項(通過Reset方法),那將不能看到被刪除的項。這也將應用在任何{除了正在讀取被刪除元素的線程之外的}所有線程。

4)  多線程同時刪除同一個項?如果沒有其他的線程正在訪問該元素,它將被安全地刪除。

5)  一個線程正在刪除一個元素,而另一個線程正在增加一個同名的元素?這依賴於哪一個線程首先獲得“鎖”。

6)  一個線程訪問一項,然後終止了。之後的某個時間,另一個線程企圖刪除這項?該項將會被刪除。

7)  一個線程刪除了一項,而另一個線程此時正在讀該項。同時第三個線程,企圖枚舉該項?該項對第三個線程將是不可見的。

8)  為什麼在API中沒有暴露Count屬性(元素的個數)?是為了避免糟糕的代碼。

為什麼安全執行緒的枚舉是重要的

我們有一個UI來列印從Dictionary集合中的值。為了讓該集合會有一些比較靈活的(或者比較新的)值,我們用一個線程來更新它。現在,使用者按下重新整理鍵。跟隨下面的圖片中基於紅色序號的事件。結果是有一個InvalidOperationException異常將被Dictionary集合的列舉程式拋出。

讓我們通過鎖住“枚舉”以及“更新”操作來解決這個異常。但是考慮這個解決方案,當使用者按下重新整理按鈕時,背景更新仍然在繼續。而UI將被掛起。這個行為是不可取的。

現在,考慮相同的情境下使用MultiMap集合。UI線程重新整理以及更新線程可以被同步執行。下面的圖片解釋了,這個情境:

下面的代碼是一個用Dictionary類做的一個簡單的例子:

MultiMap通過提供一種內建的安全執行緒機制,下面的更簡短的代碼以及解決了這個問題:

輸出:

有什麼不同

使用一個鎖住的Dictionary集合與使用MultiMap集合有什麼區別?

1)  多線程枚舉

這意味著什嗎?通常,列舉程式不能夠在一個線程上使用,當另一個線程正在更新相同的集合時。MultiMap集合則支援這樣的操作。不同的線程可以更新、修改以及枚舉MultiMap集合。

2)  線程感知列舉程式

這又意味著什嗎?考慮到Dictionary集合在一個WCF服務中使用。通常,多線程可能服務多個會話。這種情況下,Dictionary集合需要被一個線程感知的類包裹來處理“競爭條件”。

3)  資源釋放(支援Idisposable介面)

這意味著什嗎?萬一你儲存了一個不受託管的資源在MultiMap集合中,它能夠支援釋放儲存的這些資源通過一個調用。難道你不願意寫multiMapObject.Dispose()?而是願意再枚舉每一項的使用調用Dispose嗎?

補充:本文給我們提供了一個很到的思路——多線程之間的並發問題,以及.net內建類型的組合問題。你甚至可以做這樣的組合Dictionary<Key, Dictionary<Key,Value>>,來滿足你特定情境的儲存邏輯。當然,如果支援多線程,那麼並發問題,還是得考慮地比較周全。

引用:

http://blogs.msdn.com/pfxteam/archive/2008/08/12/8852005.aspx?CommentPosted=true#commentmessage

http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

原文連結:

http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

http://www.codeproject.com/KB/collections/MultiMap_P_2.aspx

樣本源碼和DLL:

http://download.csdn.net/detail/yanghua_kobe/3635173

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.