2013-02-26 71 views
12

我正在寫一個圍繞字典設計的線程安全的薄包裝。因此,需要一些鎖,大部分邏輯確保事物被正確鎖定並以線程安全的方式訪問。是否有可能嘲笑/僞造一個失蹤的鎖造成失敗的測試?

現在,我試圖單元測試它。我想單元測試的一件大事是鎖定行爲,以確保它是正確的。但是,我從來沒有在任何地方看到過這一點,所以我不知道如何去做。此外,我知道我可以使用一堆線程將東西扔到牆上,但通過這種類型的測試,不能保證它在錯誤時會失敗。這取決於OS定義的行爲與線程調度。

有什麼方法可以確保我的鎖定行爲在單元測試中是正確的?

+5

+1;好奇你爲什麼不在這個特殊情況下使用'ConcurrentDictionary'。 – 2013-02-26 19:04:38

+0

@TimMedora針對Windows Phone的便攜式類庫( – Earlz 2013-02-26 19:05:26

+0

)據我的經驗,用數以千計的操作(跨線程,多變的順序)轟炸一個推定的線程安全類會很快發現問題,但這當然不是確定性的。 – 2013-02-26 19:23:30

回答

1

我不認爲Dictionary本身你可以實現可靠的測試 - 你的目標是讓2個調用並行運行,這是不會發生可靠的。

您可以嘗試使用自定義字典(即從常規字典派生)併爲所有獲取/添加方法添加回調。根據需要,您可以在2個線程中延遲呼叫...您需要在2個線程之間單獨進行同步,以使您的測試代碼以您想要的方式運行,而不是始終處於死鎖狀態。

+0

我最終這樣做了。我詳細闡述了我是如何做到這一點的 – Earlz 2013-02-27 15:30:21

3

鎖定只是一個實現細節。你應該自己模擬競爭條件並測試數據是否在測試中沒有失去完整性。

如果您決定更改字典的實現並使用其他同步基元,這將是一大優勢。

測試鎖定將證明您正在調用您希望的鎖定語句。在模擬出的競速條件下進行測試可能會發現您不希望同步的地方。

+0

這基本上是依靠字典中斷,如果它同時訪問 – Earlz 2013-02-26 19:05:02

+0

不,它應該保持在一個正確的狀態,將由您的測試證明。 – Kimi 2013-02-26 19:09:41

+2

那麼,它依靠Dictionary的實現細節。我實際上需要測試Dictionary只是從來沒有嘗試以併發方式訪問,因爲整個事情基本上對我的目的來說基本上不是線程安全的 – Earlz 2013-02-26 19:10:39

2

我基本上最終做了@Alexei說的。更具體地講,這是我做的:

  1. 做一個PausingDictionary這基本上只是對每一個方法的回調,但在其他方面只是通過一個普通的字典
  2. 摘要我的代碼(使用DI等),以便在測試時可以使用PausingDictionary而不是常規字典
  3. 在我的單元測試中添加了兩個ConcurrentDictionaries。一個叫「訪問」,一個叫「GoAhead」。到thiis的關鍵是增加了一些擴展方法,使與它的工作更容易一點
  4. 設置字典的回調裝置接入了的"action"+Thread.GetHashCode().ToString()組合(其中動作是不同的每個回調)
  5. 初始化一切爲假線程爲真,但它會在回調中等待,直到GoAhead爲真
  6. 從單元測試中啓動兩個線程。一個線程可以訪問字典,但因爲GoAhead對於該線程爲false,它會坐在那裏。然後第二個線程將嘗試訪問字典
  7. 我會斷言該線程的Accessed是錯誤的,因爲我的代碼應該鎖定它。

還有一點比它。我還需要模擬一個IList,但我認爲我不會。這些單元測試雖然有價值,但絕對不是世界上最容易編寫的東西。除了設置代碼和假接口實現等外,每個測試最終還是大約25行非模板代碼。鎖定很難。證明你的鎖定是有效的更難。令人驚訝的是,這種模式可以讓你測試幾乎任何場景。但是,它非常冗長,並且不能用於漂亮的測試

因此,儘管很難編寫測試,但它完美地工作。當我刪除一個鎖始終失敗,並且當我添加鎖時,它始終通過。

編輯:

我覺得線程的「控制交錯」的這種方法也將有可能測試線程安全的,因爲你寫的每一個可能的交錯測試。用一些代碼這是不可能的,但我只是想說,這絕不僅限於鎖定代碼。你可以用同樣的方法在問題的概念上一致地複製線程安全失敗,如foo.Contains(x),然後var tmp=foo[x]

+0

+1:非常好的寫法! – 2013-02-27 17:06:36

相關問題