マルチスレッドコードの競合状態とデッドロックを単体テストする方法はありますか?
彼らがあるべき姿を実行しているかどうかを確認するには...
[〜#〜] chess [〜#〜] 、Microsoft Researchのプロジェクト。彼らのサイトの引用:
CHESSは、並行プログラムでハイゼンバグを見つけて再現するためのツールです。 CHESSは、同時テストを繰り返し実行して、すべての実行で異なるインターリーブが実行されることを確認します。インターリーブの結果としてエラーが発生した場合、CHESSはインターリーブを再現してデバッグを改善できます。 CHESSは、マネージプログラムとネイティブプログラムの両方で使用できます。
Update(9/23/2015):C、C++、およびGoの場合、 ThreadSanitizer を使用できます。
Valgrind には Helgrind があり、本当に役立ちます。飢餓やデッドロックにつながる可能性のあるレースを指摘するのに役立つだけでなく、プログラムのプロファイルを作成することのわずかな速度低下により、他の方法では見られないレースが明らかになる場合があります。
だから、あなたが何らかのロックフリーの方法でコマンドーに行ったとしても、それはまだ役立ちます:)
ただし、POSIX中心です。 TAPのような単純な単体テストライブラリが実行されていることを簡単に認識できるヘッダーが同梱されており、これも非常に役立ちます。たとえば、飢餓をシミュレートするために、ロックを取得しようとしたときに通常はブロックされないスレッドを先に進めて(おそらくランダムに)ブロックすることができます。
詳細は正確には覚えていませんが、これは一般的な考え方です。そして、私はそれを1度だけ行いましたが、私がしたことは、タスククラスをモックできるようにインターフェイスを使用して、タスクを実行するコードからリエントラントコードを分離することでした。
次に、スレッドがクリティカルセクションにあることを確認できるようにモックアップを設計し、スレッドがクリティカルセクションにあることを確認してから、もう一度呼び出して待機していることを確認してから、最初のスレッドを解放し、正常に終了しました。
そんな感じ。
これがより複雑なシナリオで機能するかどうかはわかりませんが、リファクタリング中の動作を維持するのに役立ちます。
今年のJAOO/GOTOでこのプレゼンテーションを見ました。
トリックは、呼び出し手順とアプリケーションの実際の操作の観点から、ヘアボールアプリケーションが実行することになっているものをモデル化することです。次に、John Hughesソフトウェアは、parallelで呼び出し手順の多くの順列を繰り返し試行し、その後、アプリケーションの状態がモデルの状態に対応していることを確認します。エラーが見つかった場合、ソフトウェアは、エラーを生成する最小限のケースにステップを減らす方法を知っています。
彼は15年間潜伏していて時々報告されているコアErlangライブラリのいくつかのバグをキャッチする方法をライブで示しましたが、それらがどこから来たのか、したがって修正方法を理解することはできませんでした。ソフトウェアによって報告された最小限のケースで、ライブラリーのメンテナーはday以内に各バグを修正することができました。
それはSO印象的でした。
John Hughesはこのソフトウェアを彼の会社を通じて販売しています。
私の Relacy Race Detector を試してみてください。プロデューサー/コンシューマーキューや並行コンテナーなどの同期アルゴリズムを慎重かつ正確に検証するように設計されていますが、プログラム全体の検証にはあまり適していません。ただし、同期とミューテックスをプログラム全体に分散させることをお勧めしますが、代わりに(Relacyで検証できる)専用コンポーネントに同期を集中させることをお勧めします。
簡単ではありませんが、基本的には、マルチスレッドコードを複数のスレッドから同時に呼び出すおよびタイミングと順序をランダムに変更するランダムThread.sleep()
およびThread.yield()
呼び出し(Javaを想定)。
上記のようなことを行うツール(TestNGなど)も用意されていますが、私が知る限り、まだ十分に成熟していません。
厳密な単体テストではありませんが、テストが断続的に失敗するテストに役立つランタイムチェックです。それは速くて汚いですが、うまくいきました。
Mutexが許可されると、どのスレッドがそれを持っているかを追跡します。すべてのmutexリクエストには30秒のタイムアウトがあり、その後、デッドロックを叫びます。
次に、許可されたミューテックスのリストを使用して、ブロッキングミューテックスを保持しているスレッド、およびその理由を確認できます。これまでの私のケースでは、それは何か他のものに行き詰まっているためです。それで、その状況を修正することができます。
ミューテックスにはクロスプラットフォームのラッパークラスがあり、レコードの保持とタイムアウトを簡単に挿入できるため、これは私にとってはうまくいきました。また、ミューテックスで30秒間ブロックされてはならないことを知るのに十分なアプリケーションについても知っていました。
これは完全に汎用的な目的ではないかもしれませんが、約2時間のプログラミング作業で多くのデバッグを節約できます。オーバーヘッドはごくわずかで、デバッグビルドのみが可能です。
ネストされたミューテックス要求シーケンスを記録するためにそれを拡張することを見て、実際にデッドロックを引き起こすだけでなく、デッドロックを引き起こす可能性があるものがあるかどうかを確認します(たとえば、1つのスレッドがA、Bをロックし、別のスレッドがB、Aをロックします)。ささいな努力のために多くの利益をもたらしました。