12

スレッドセーフになるように設計された Dictionary の薄いラッパーを作成しています。そのため、いくつかのロックが必要であり、大部分のロジックは、物事が適切にロックされ、スレッドセーフな方法でアクセスされるようにするためのものです。

今、私はそれを単体テストしようとしています。単体テストを行いたいのは、ロックの動作が正しいことを確認することです。ただし、これがどこでも行われているのを見たことがないので、どうすればよいかわかりません。また、たくさんのスレッドを使用して壁に物を投げることができることも知っていますが、このタイプのテストでは、間違っているときに失敗するという保証はありません。スレッドスケジューリングでのOS定義の動作次第です。

単体テストでロック動作が正しいことを確認するには、どのような方法がありますか?

4

3 に答える 3

3

ロックは単なる実装の詳細です。競合状態自体をモックアップし、テストでデータの整合性が失われていないかどうかをテストする必要があります。

これは、ディクショナリの実装を変更して他の同期プリミティブを使用する場合に役立ちます。

ロックをテストすると、ロック ステートメントを予期している場所で呼び出していることが証明されます。モックアップされた競合状態でテストすると、同期することを予期していなかった場所が明らかになる可能性があります。

于 2013-02-26T19:03:49.430 に答える
2

私は基本的に@Alexeiが言ったことをやることになりました。より具体的には、これは私がしたことです:

  1. 基本PausingDictionary的にすべてのメソッドのコールバックを持つだけの a を作成しますが、それ以外の場合は通常の辞書に渡すだけです
  2. テスト時に通常の Dictionary の代わりに PausingDictionary を使用できるように、コードを (DI などを使用して) 抽象化します。
  3. 単体テストに 2 つの ConcurrentDictionaries を追加しました。1 つは「Accessed」と呼ばれ、もう 1 つは「GoAhead」と呼ばれます。これの鍵はの組み合わせです"action"+Thread.GetHashCode().ToString()(アクションはコールバックごとに異なります)
  4. すべてをfalseに初期化し、いくつかの拡張メソッドを追加して、操作を少し簡単にしました
  5. 辞書のコールバックをセットアップして、スレッドの Accessed を true に設定しますが、GoAhead が true になるまでコールバックで待機します。
  6. 単体テスト内から 2 つのスレッドを開始しました。1 つのスレッドがディクショナリにアクセスしますGoAheadが、そのスレッドでは が false であるため、そこに留まります。次に、2 番目のスレッドもディクショナリにアクセスしようとします。
  7. Accessed私のコードはそれをロックアウトする必要があるため、そのスレッドはfalseであるというアサーションがあります。

それだけではありません。IList もモックアップする必要がありますが、そうは思いません。これらの単体テストは価値がありますが、間違いなく世界で最も簡単に作成できるものではありません。セットアップ コードや偽のインターフェイスの実装などを除けば、各テストは約 25 行の非定型コードになります。ロックが難しい。ロックが効果的であることを証明することはさらに困難です。驚くべきことに、この種のパターンを使用すると、ほぼすべてのシナリオをテストできます。しかし、それは非常に冗長で、きれいなテストにはなりません

したがって、テストを書くのは大変ですが、これは完璧に機能します。ロックを削除すると一貫して失敗し、ロックを追加すると一貫して成功します。

編集:

スレッドの「インターリーブを制御する」この方法は、可能なインターリーブごとにテストを作成することを考えると、スレッドセーフをテストすることも可能になると思います。一部のコードではこれは不可能ですが、コードをロックするだけに限定されているわけではないと言いたいだけです。次のようなスレッドセーフな失敗を一貫して複製するために同じ方法を行うことができfoo.Contains(x)ますvar tmp=foo[x]

于 2013-02-27T15:29:48.550 に答える
1

私はそれ自体では信頼できるテストを達成できるとは思いませんDictionary-あなたの目標は並行して実行するために2つの呼び出しを行うことですが、これは確実には起こりません。

カスタムディクショナリ(つまり、通常のディクショナリから派生)を作成して、すべてのGet/Addメソッドにコールバックを追加することができます。必要に応じて2つのスレッドで呼び出しを遅らせることができるよりも...テストコードを希望どおりに実行し、常にデッドロックしないようにするには、2つのスレッド間で個別に同期する必要があります。

于 2013-02-26T19:17:34.393 に答える