我正在寫一個圍繞字典設計的線程安全的薄包裝。因此,需要一些鎖,大部分邏輯確保事物被正確鎖定並以線程安全的方式訪問。是否有可能嘲笑/僞造一個失蹤的鎖造成失敗的測試?
現在,我試圖單元測試它。我想單元測試的一件大事是鎖定行爲,以確保它是正確的。但是,我從來沒有在任何地方看到過這一點,所以我不知道如何去做。此外,我知道我可以使用一堆線程將東西扔到牆上,但通過這種類型的測試,不能保證它在錯誤時會失敗。這取決於OS定義的行爲與線程調度。
有什麼方法可以確保我的鎖定行爲在單元測試中是正確的?
我正在寫一個圍繞字典設計的線程安全的薄包裝。因此,需要一些鎖,大部分邏輯確保事物被正確鎖定並以線程安全的方式訪問。是否有可能嘲笑/僞造一個失蹤的鎖造成失敗的測試?
現在,我試圖單元測試它。我想單元測試的一件大事是鎖定行爲,以確保它是正確的。但是,我從來沒有在任何地方看到過這一點,所以我不知道如何去做。此外,我知道我可以使用一堆線程將東西扔到牆上,但通過這種類型的測試,不能保證它在錯誤時會失敗。這取決於OS定義的行爲與線程調度。
有什麼方法可以確保我的鎖定行爲在單元測試中是正確的?
我不認爲Dictionary
本身你可以實現可靠的測試 - 你的目標是讓2個調用並行運行,這是不會發生可靠的。
您可以嘗試使用自定義字典(即從常規字典派生)併爲所有獲取/添加方法添加回調。根據需要,您可以在2個線程中延遲呼叫...您需要在2個線程之間單獨進行同步,以使您的測試代碼以您想要的方式運行,而不是始終處於死鎖狀態。
我最終這樣做了。我詳細闡述了我是如何做到這一點的 – Earlz 2013-02-27 15:30:21
鎖定只是一個實現細節。你應該自己模擬競爭條件並測試數據是否在測試中沒有失去完整性。
如果您決定更改字典的實現並使用其他同步基元,這將是一大優勢。
測試鎖定將證明您正在調用您希望的鎖定語句。在模擬出的競速條件下進行測試可能會發現您不希望同步的地方。
我基本上最終做了@Alexei說的。更具體地講,這是我做的:
PausingDictionary
這基本上只是對每一個方法的回調,但在其他方面只是通過一個普通的字典"action"+Thread.GetHashCode().ToString()
組合(其中動作是不同的每個回調)GoAhead
對於該線程爲false,它會坐在那裏。然後第二個線程將嘗試訪問字典Accessed
是錯誤的,因爲我的代碼應該鎖定它。還有一點比它。我還需要模擬一個IList,但我認爲我不會。這些單元測試雖然有價值,但絕對不是世界上最容易編寫的東西。除了設置代碼和假接口實現等外,每個測試最終還是大約25行非模板代碼。鎖定很難。證明你的鎖定是有效的更難。令人驚訝的是,這種模式可以讓你測試幾乎任何場景。但是,它非常冗長,並且不能用於漂亮的測試
因此,儘管很難編寫測試,但它完美地工作。當我刪除一個鎖始終失敗,並且當我添加鎖時,它始終通過。
編輯:
我覺得線程的「控制交錯」的這種方法也將有可能測試線程安全的,因爲你寫的每一個可能的交錯測試。用一些代碼這是不可能的,但我只是想說,這絕不僅限於鎖定代碼。你可以用同樣的方法在問題的概念上一致地複製線程安全失敗,如foo.Contains(x)
,然後var tmp=foo[x]
+1:非常好的寫法! – 2013-02-27 17:06:36
+1;好奇你爲什麼不在這個特殊情況下使用'ConcurrentDictionary'。 – 2013-02-26 19:04:38
@TimMedora針對Windows Phone的便攜式類庫( – Earlz 2013-02-26 19:05:26
)據我的經驗,用數以千計的操作(跨線程,多變的順序)轟炸一個推定的線程安全類會很快發現問題,但這當然不是確定性的。 – 2013-02-26 19:23:30