これまでのところ、マルチスレッド コードをテストするという悪夢を避けてきました。正常に実行するためにスレッドに依存するコードのテストについて、人々はどのように取り組んでいるのか、または 2 つのスレッドが特定の方法で相互作用する場合にのみ現れるこの種の問題のテストについて、人々はどのように取り組んでいるのかを尋ねたいと思います。
これは、今日のプログラマーにとって非常に重要な問題のように思えます。
これまでのところ、マルチスレッド コードをテストするという悪夢を避けてきました。正常に実行するためにスレッドに依存するコードのテストについて、人々はどのように取り組んでいるのか、または 2 つのスレッドが特定の方法で相互作用する場合にのみ現れるこの種の問題のテストについて、人々はどのように取り組んでいるのかを尋ねたいと思います。
これは、今日のプログラマーにとって非常に重要な問題のように思えます。
ほら、これを行う簡単な方法はありません。私は本質的にマルチスレッドであるプロジェクトに取り組んでいます。イベントはオペレーティングシステムから着信し、同時に処理する必要があります。
複雑なマルチスレッドアプリケーションコードのテストに対処する最も簡単な方法は次のとおりです。テストするには複雑すぎる場合は、間違っています。複数のスレッドが作用する単一のインスタンスがあり、これらのスレッドが相互にステップオーバーする状況をテストできない場合は、設計をやり直す必要があります。これと同じくらい単純で複雑です。
インスタンスを同時に通過するスレッドを回避するマルチスレッドをプログラムする方法はたくさんあります。最も簡単なのは、すべてのオブジェクトを不変にすることです。もちろん、それは通常は不可能です。したがって、スレッドが同じインスタンスと相互作用するデザイン内の場所を特定し、それらの場所の数を減らす必要があります。これを行うことで、マルチスレッドが実際に発生するいくつかのクラスを分離し、システムのテストの全体的な複雑さを軽減します。
ただし、これを行っても、2つのスレッドが互いにステップするすべての状況をテストできるわけではないことを理解する必要があります。これを行うには、同じテストで2つのスレッドを同時に実行してから、任意の時点で実行されている行を正確に制御する必要があります。あなたができる最善のことは、この状況をシミュレートすることです。しかし、これにはテスト用に特別にコーディングする必要があるかもしれません。それはせいぜい真のソリューションに向けた半分のステップです。
おそらく、スレッドの問題についてコードをテストする最良の方法は、コードの静的分析を使用することです。スレッド化されたコードがスレッドセーフパターンの有限集合に従わない場合は、問題が発生している可能性があります。VSのコード分析にはスレッド化の知識がある程度含まれていると思いますが、おそらくそれほど多くはありません。
見てください、現在の状況(そしておそらくこれからの良い時期になるでしょう)として、マルチスレッドアプリをテストする最良の方法は、スレッドコードの複雑さを可能な限り減らすことです。スレッドが相互作用する領域を最小限に抑え、可能な限りテストし、コード分析を使用して危険な領域を特定します。
この質問が投稿されてからしばらく経ちましたが、まだ回答がありません...
kleolb02の答えは良いものです。詳しくみていきます。
私が C# コードで実践している方法があります。単体テストの場合、再現可能なテストをプログラムできる必要があります。これは、マルチスレッド コードの最大の課題です。したがって、私の回答は、同期的に動作するテスト ハーネスに非同期コードを強制することを目的としています。
これは、Gerard Meszaros の著書「xUnit Test Patterns」からのアイデアであり、「Humble Object」(p. 695) と呼ばれています。コア ロジック コードと非同期コードの匂いがするものを互いに分離する必要があります。これにより、同期的に動作するコア ロジックのクラスが作成されます。
これにより、コア ロジック コードを同期的にテストできるようになります。コアロジックで実行している呼び出しのタイミングを完全に制御できるため、再現可能なテストを作成できます。これは、コア ロジックと非同期ロジックを分離することによるメリットです。
このコア ロジックは、コア ロジックへの呼び出しを非同期で受信し、これらの呼び出しをコア ロジックに委譲する別のクラスでラップする必要があります。プロダクション コードは、そのクラスを介してコア ロジックにのみアクセスします。このクラスは呼び出しのみを委任する必要があるため、多くのロジックを持たない非常に「ばかげた」クラスです。したがって、この非同期作業クラスの単体テストを最小限に抑えることができます。
それより上 (クラス間の相互作用のテスト) はコンポーネント テストです。この場合も、「Humble Object」パターンに固執すれば、タイミングを完全に制御できるはずです。
確かにタフなもの!私の(C ++)単体テストでは、これを使用した同時実行パターンに沿っていくつかのカテゴリに分類しました。
シングルスレッドで動作し、スレッドを認識しないクラスの単体テスト-簡単、通常どおりテストします。
同期されたパブリックAPIを公開するMonitorオブジェクト(呼び出し元の制御スレッドで同期されたメソッドを実行するオブジェクト)の単体テスト-APIを実行する複数のモックスレッドをインスタンス化します。パッシブオブジェクトの内部条件を実行するシナリオを作成します。基本的に、長期間にわたって複数のスレッドからのテストを打ち負かす、より長時間実行されるテストを1つ含めます。これは私が知っている非科学的ですが、それは自信を築きます。
アクティブオブジェクト(独自のスレッドまたは制御スレッドをカプセル化するオブジェクト)の単体テスト-上記の#2と同様ですが、クラスの設計によって異なります。パブリックAPIはブロックまたは非ブロックである可能性があり、呼び出し元は先物を取得する可能性があり、データはキューに到着するか、キューから削除する必要があります。ここでは多くの組み合わせが可能です。白い箱を離れて。テスト対象のオブジェクトを呼び出すには、複数のモックスレッドが必要です。
余談として:
私が行っている内部開発者トレーニングでは、並行性の問題について考え、分解するための主要なフレームワークとして、並行性の柱とこれら2つのパターンを教えています。明らかにもっと高度な概念がありますが、この一連の基本はエンジニアをスープから遠ざけるのに役立つことがわかりました。また、上記のように、よりユニットテスト可能なコードにつながります。
ここ数年、いくつかのプロジェクトのスレッド処理コードを書いているときに、この問題に何度か直面しました。他の回答のほとんどは、代替案を提供しているが、実際にはテストに関する質問に回答していないため、回答が遅れています。私の答えは、マルチスレッド コードに代わるものがない場合です。完全を期すためにコード設計の問題を取り上げますが、単体テストについても説明します。
テスト可能なマルチスレッド コードの記述
最初に行うことは、実際のデータ処理を行うすべてのコードから本番スレッド処理コードを分離することです。そうすれば、データ処理をシングル スレッド コードとしてテストでき、マルチスレッド コードで実行されるのはスレッドの調整だけです。
2 番目に覚えておくべきことは、マルチスレッド コードのバグは確率的であることです。発生頻度が最も低いバグは、本番環境に忍び込み、本番環境でも再現が難しく、したがって最大の問題を引き起こすバグです。このため、コードをすばやく作成し、それが機能するまでデバッグするという標準的なコーディング アプローチは、マルチスレッド コードには適していません。簡単なバグが修正され、危険なバグがまだ残っているコードになります。
代わりに、マルチスレッド コードを作成するときは、最初からバグを作成しないようにするという態度でコードを作成する必要があります。データ処理コードを適切に削除した場合、スレッド処理コードは十分に小さく (できれば数行、最悪でも数十行)、バグを書かずに、そして確実に多くのバグを書かずにコードを作成できる可能性があります。 、スレッドを理解している場合は、時間をかけて注意してください。
マルチスレッド コードの単体テストの記述
マルチスレッド コードをできる限り慎重に記述した後でも、そのコードのテストを記述する価値はあります。テストの主な目的は、タイミングに大きく依存する競合状態のバグをテストすることではなく、そのような競合状態を繰り返しテストすることは不可能です。むしろ、そのようなバグを防止するためのロック戦略が複数のスレッドが意図したとおりに相互作用できることをテストすることです。 .
正しいロック動作を適切にテストするには、テストで複数のスレッドを開始する必要があります。テストを再現可能にするために、スレッド間の相互作用が予測可能な順序で発生するようにします。テストではスレッドを外部同期したくありません。これは、スレッドが外部同期されていない本番環境で発生する可能性のあるバグを隠してしまうためです。これは、マルチスレッド コードのテストを作成しなければならないときはいつでも、私が成功裏に使用してきた手法です。
遅延が短すぎると、テストが脆弱になります。たとえば、テストを実行する異なるマシン間でのわずかなタイミングの違いにより、タイミングがずれてテストが失敗する可能性があるためです。私が通常行ったことは、テストの失敗を引き起こす遅延から始め、開発マシンでテストが確実にパスするように遅延を増やし、それを超えると遅延を 2 倍にして、テストが他のマシンでパスする可能性を高めます。これは、テストに巨視的な時間がかかることを意味しますが、私の経験では、慎重なテスト設計により、その時間を数十秒以内に制限できます。アプリケーションでスレッド調整コードを必要とする場所はそれほど多くないはずなので、テスト スイートでは許容できるはずです。
最後に、テストで検出されたバグの数を追跡します。テストのコード カバレッジが 80% の場合、バグの約 80% を捕捉できると期待できます。テストが適切に設計されていてもバグが見つからない場合は、本番環境でのみ現れる追加のバグがない可能性が十分にあります。テストで 1 つまたは 2 つのバグが見つかった場合でも、幸運に恵まれる可能性があります。それを超えて、スレッド処理コードの慎重なレビューまたは完全な書き直しを検討することをお勧めします。これは、コードが本番環境になるまで見つけるのが非常に困難な隠れたバグがコードにまだ含まれている可能性があるためです。それから修正するのは難しいです。
また、マルチスレッド コードのテストにも深刻な問題がありました。その後、Gerard Meszaros による「xUnit Test Patterns」で非常に優れたソリューションを見つけました。彼が説明するパターンはHumble objectと呼ばれます。
基本的に、環境から切り離された、テストしやすい個別のコンポーネントにロジックを抽出する方法について説明します。このロジックをテストしたら、複雑な動作 (マルチスレッド、非同期実行など) をテストできます。
かなり優れたツールがいくつかあります。Java のいくつかの概要を次に示します。
いくつかの優れた静的分析ツールには、FindBugs (いくつかの役立つヒントを提供します)、JLint、Java Pathfinder (JPF & JPF2)、およびBogorが含まれます。
MultithreadedTCは非常に優れた動的分析ツール (JUnit に統合されている) であり、独自のテスト ケースを設定する必要があります。
IBM Researchの ConTestは興味深いものです。あらゆる種類のスレッド変更動作 (スリープ & イールドなど) を挿入してコードを計測し、バグをランダムに発見しようとします。
SPINは、Java (およびその他の) コンポーネントをモデル化するための非常に優れたツールですが、便利なフレームワークが必要です。そのままでは使いづらいですが、使いこなせば非常に強力です。かなりの数のツールが内部で SPIN を使用しています。
MultithreadedTC はおそらく最も主流ですが、上記の静的解析ツールのいくつかは一見の価値があります。
待機性は、決定論的な単体テストを作成するのにも役立ちます。システムのどこかの状態が更新されるまで待つことができます。例えば:
await().untilCall( to(myService).myMethod(), greaterThan(3) );
また
await().atMost(5,SECONDS).until(fieldIn(myObject).ofType(int.class), equalTo(1));
また、Scala と Groovy もサポートしています。
await until { something() > 4 } // Scala example
スレッド化されたコードや非常に複雑なシステムを一般的にテストするもう 1 つの方法は、Fuzz Testingを使用することです。それは素晴らしいものではなく、すべてを見つけることはできませんが、便利で簡単に実行できる可能性があります。
見積もり:
ファズ テストまたはファジングは、プログラムの入力にランダム データ (「ファズ」) を提供するソフトウェア テスト手法です。プログラムが失敗した場合 (たとえば、クラッシュや組み込みコード アサーションの失敗など)、欠陥に注意することができます。ファズ テストの大きな利点は、テストの設計が非常に単純であり、システムの動作に関する先入観がないことです。
...
ファズ テストは、ブラック ボックス テストを使用する大規模なソフトウェア開発プロジェクトでよく使用されます。これらのプロジェクトは、通常、テスト ツールを開発するための予算を持っています。ファズ テストは、費用対効果の高い利点を提供する手法の 1 つです。
...
ただし、ファズ テストは、徹底的なテストや正式な方法の代わりにはなりません。システムの動作の無作為なサンプルしか提供できず、多くの場合、ファズ テストに合格しても、ソフトウェアの一部がクラッシュせずに例外を処理することを示すだけです。正しく動作します。したがって、ファズ テストは、品質の保証ではなく、バグ検出ツールと見なすことしかできません。
私はこれをたくさんしました、そしてはい、それはひどいです。
いくつかのヒント:
throwable
てチェックインしますtearDown
(リスト 1 を参照)。別のスレッドで悪い例外をキャッチした場合は、それを throwable に割り当てます。AtomicBoolean
テストで有効に活用してください。これはスレッド セーフであり、コールバック クラスなどからの値を格納するために最終的な参照型が必要になることがよくあります。リスト 3 の例を参照してください。@Test(timeout=60*1000)
並行性テストは、壊れたときに永久にハングアップすることがあるため、常にテストにタイムアウトを与えるようにしてください (例: )。リスト 1:
@After
public void tearDown() {
if ( throwable != null )
throw throwable;
}
リスト 2:
import static org.junit.Assert.fail;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Random;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.time.StopWatch;
import org.easymock.EasyMock;
import org.easymock.classextension.internal.ClassExtensionHelper;
import static org.easymock.classextension.EasyMock.*;
import ca.digitalrapids.io.DRFileUtils;
/**
* Various utilities for testing
*/
public abstract class DRTestUtils
{
static private Random random = new Random();
/** Calls {@link #waitForCondition(Integer, Integer, Predicate, String)} with
* default max wait and check period values.
*/
static public void waitForCondition(Predicate predicate, String errorMessage)
throws Throwable
{
waitForCondition(null, null, predicate, errorMessage);
}
/** Blocks until a condition is true, throwing an {@link AssertionError} if
* it does not become true during a given max time.
* @param maxWait_ms max time to wait for true condition. Optional; defaults
* to 30 * 1000 ms (30 seconds).
* @param checkPeriod_ms period at which to try the condition. Optional; defaults
* to 100 ms.
* @param predicate the condition
* @param errorMessage message use in the {@link AssertionError}
* @throws Throwable on {@link AssertionError} or any other exception/error
*/
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms,
Predicate predicate, String errorMessage) throws Throwable
{
waitForCondition(maxWait_ms, checkPeriod_ms, predicate, new Closure() {
public void execute(Object errorMessage)
{
fail((String)errorMessage);
}
}, errorMessage);
}
/** Blocks until a condition is true, running a closure if
* it does not become true during a given max time.
* @param maxWait_ms max time to wait for true condition. Optional; defaults
* to 30 * 1000 ms (30 seconds).
* @param checkPeriod_ms period at which to try the condition. Optional; defaults
* to 100 ms.
* @param predicate the condition
* @param closure closure to run
* @param argument argument for closure
* @throws Throwable on {@link AssertionError} or any other exception/error
*/
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms,
Predicate predicate, Closure closure, Object argument) throws Throwable
{
if ( maxWait_ms == null )
maxWait_ms = 30 * 1000;
if ( checkPeriod_ms == null )
checkPeriod_ms = 100;
StopWatch stopWatch = new StopWatch();
stopWatch.start();
while ( !predicate.evaluate(null) ) {
Thread.sleep(checkPeriod_ms);
if ( stopWatch.getTime() > maxWait_ms ) {
closure.execute(argument);
}
}
}
/** Calls {@link #waitForVerify(Integer, Object)} with <code>null</code>
* for {@code maxWait_ms}
*/
static public void waitForVerify(Object easyMockProxy)
throws Throwable
{
waitForVerify(null, easyMockProxy);
}
/** Repeatedly calls {@link EasyMock#verify(Object[])} until it succeeds, or a
* max wait time has elapsed.
* @param maxWait_ms Max wait time. <code>null</code> defaults to 30s.
* @param easyMockProxy Proxy to call verify on
* @throws Throwable
*/
static public void waitForVerify(Integer maxWait_ms, Object easyMockProxy)
throws Throwable
{
if ( maxWait_ms == null )
maxWait_ms = 30 * 1000;
StopWatch stopWatch = new StopWatch();
stopWatch.start();
for(;;) {
try
{
verify(easyMockProxy);
break;
}
catch (AssertionError e)
{
if ( stopWatch.getTime() > maxWait_ms )
throw e;
Thread.sleep(100);
}
}
}
/** Returns a path to a directory in the temp dir with the name of the given
* class. This is useful for temporary test files.
* @param aClass test class for which to create dir
* @return the path
*/
static public String getTestDirPathForTestClass(Object object)
{
String filename = object instanceof Class ?
((Class)object).getName() :
object.getClass().getName();
return DRFileUtils.getTempDir() + File.separator +
filename;
}
static public byte[] createRandomByteArray(int bytesLength)
{
byte[] sourceBytes = new byte[bytesLength];
random.nextBytes(sourceBytes);
return sourceBytes;
}
/** Returns <code>true</code> if the given object is an EasyMock mock object
*/
static public boolean isEasyMockMock(Object object) {
try {
InvocationHandler invocationHandler = Proxy
.getInvocationHandler(object);
return invocationHandler.getClass().getName().contains("easymock");
} catch (IllegalArgumentException e) {
return false;
}
}
}
リスト 3:
@Test
public void testSomething() {
final AtomicBoolean called = new AtomicBoolean(false);
subject.setCallback(new SomeCallback() {
public void callback(Object arg) {
// check arg here
called.set(true);
}
});
subject.run();
assertTrue(called.get());
}
すでに述べたように、MT コードの正確性をテストすることは非常に難しい問題です。最終的には、コードに誤って同期されたデータ競合がないようにすることです。これに関する問題は、スレッド実行 (インターリービング) の可能性が無数にあることです (ただし、この記事を必ず読んでください)。単純なシナリオでは、推論によって正確性を実際に証明できる場合がありますが、通常はそうではありません。特に、同期を回避/最小化し、最も明白/最も簡単な同期オプションを使用したくない場合。
私が従うアプローチは、検出されない可能性のあるデータ競合が発生する可能性を高めるために、高度に並行性のあるテスト コードを記述することです。そして、私はしばらくの間それらのテストを実行します:)私はかつて、コンピューター科学者がこれを行うツールを披露している話を偶然見つけました(仕様からランダムにテストを考案し、それらを乱暴に同時に実行し、定義された不変条件をチェックします)壊れる)。
ところで、MT コードのテストのこの側面については、ここでは触れていないと思います。ランダムにチェックできるコードの不変条件を特定します。残念ながら、これらの不変条件を見つけることも非常に難しい問題です。また、実行中に常に保持されるとは限らないため、それらが真であると期待できる実行ポイントを見つけて強制する必要があります。コードの実行をそのような状態にすることも難しい問題です (そして、それ自体が並行性の問題を引き起こす可能性があります。
読むべきいくつかの興味深いリンク:
スレッド化されたコンポーネントの単体テストは、通常の単体テストと同じ方法で処理します。つまり、制御と分離フレームワークの反転を使用します。私は .Net-arena で開発を行っていますが、箱から出してすぐに、(とりわけ) スレッド化を完全に分離するのは非常に困難です (ほぼ不可能と言えます)。
したがって、次のようなラッパーを作成しました (簡略化)。
public interface IThread
{
void Start();
...
}
public class ThreadWrapper : IThread
{
private readonly Thread _thread;
public ThreadWrapper(ThreadStart threadStart)
{
_thread = new Thread(threadStart);
}
public Start()
{
_thread.Start();
}
}
public interface IThreadingManager
{
IThread CreateThread(ThreadStart threadStart);
}
public class ThreadingManager : IThreadingManager
{
public IThread CreateThread(ThreadStart threadStart)
{
return new ThreadWrapper(threadStart)
}
}
そこから、IThreadingManager をコンポーネントに簡単に挿入し、選択した分離フレームワークを使用して、テスト中に期待どおりにスレッドを動作させることができます。
これまでのところ、私にとってはうまく機能しており、スレッドプール、System.Environment、Sleep などに同じアプローチを使用しています。
Javaについては、 JCIPの第12章を確認してください。少なくとも並行コードの正当性と不変条件をテストするために、決定論的なマルチスレッド単体テストを作成する具体的な例がいくつかあります。
単体テストでスレッドセーフを「証明」するのは非常に困難です。私の信念は、これはさまざまなプラットフォーム/構成での自動統合テストによってより適切に機能するということです。
並列スレッドで実行する2つ以上のテストメソッドを作成するのが好きで、それぞれがテスト対象のオブジェクトを呼び出します。私はSleep()呼び出しを使用して、さまざまなスレッドからの呼び出しの順序を調整してきましたが、それは実際には信頼できません。また、タイミングが通常機能するのに十分な時間スリープする必要があるため、非常に遅くなります。
FindBugsを書いたのと同じグループからマルチスレッドTCJavaライブラリを見つけました。Sleep()を使用せずにイベントの順序を指定でき、信頼性があります。まだ試していません。
このアプローチの最大の制限は、問題が発生すると思われるシナリオのみをテストできることです。他の人が言っているように、マルチスレッドコードを少数の単純なクラスに分離して、それらを徹底的にテストすることを期待する必要があります。
トラブルを引き起こすと予想されるシナリオを注意深くテストしたら、クラスで同時に大量のリクエストをスローする非科学的なテストは、予期しないトラブルを探すための良い方法です。
更新:マルチスレッドTC Javaライブラリで少し遊んだことがありますが、うまく機能します。また、その機能の一部を、 TickingTestと呼ばれる.NETバージョンに移植しました。
私の関連する答えを見てください
Java に偏っていますが、オプションの妥当な要約があります。
要約すると(IMO)、正確性を保証する派手なフレームワークを使用するのではなく、マルチスレッドコードをどのように設計するかです。懸念事項 (同時実行性と機能性) を分割することは、自信を高める上で非常に役立ちます。Growing Object Orientated Software Guided By Testsでは、いくつかのオプションについて、私が説明している以上に詳しく説明しています。
静的分析と正式なメソッド ( Concurrency: State Models and Java Programsを参照) はオプションですが、商用開発では使用が制限されていることがわかりました。
load/soak スタイルのテストでは、問題が浮き彫りになることはめったにないことを忘れないでください。
幸運を!
Pete Goodliffeは、スレッド化されたコードの単体テストに関する連載を行っています。
それは難しい。私はより簡単な方法を取り、実際のテストからスレッド化コードを抽象化するようにしています。ピートは私のやり方が間違っていると言っていますが、私は正しく分離できたか、運が良かったかのどちらかです。
最近、(Java 用に) Threadsafe というツールを発見しました。これは findbugs によく似た静的分析ツールですが、特にマルチスレッドの問題を発見するためのものです。テストに代わるものではありませんが、信頼性の高いマルチスレッド Java を作成する一環としてお勧めできます。
クラスの包含、同時実行クラスを介した安全でないオブジェクトへのアクセス、ダブル チェック ロック パラダイムを使用した場合の volatile 修飾子の欠落など、いくつかの非常に微妙な潜在的な問題も検出します。
マルチスレッド Java を作成する場合は、試してみてください。
次の記事では、2 つの解決策を提案しています。セマフォ (CountDownLatch) をラップし、内部スレッドからデータを外部化するなどの機能を追加します。この目的を達成するもう 1 つの方法は、スレッド プールを使用することです (関心のあるポイントを参照)。
私はスレッド化されたコードをテストするという不運な仕事をしましたが、それらは私がこれまでに書いたテストの中で間違いなく最も難しいものです。
テストを作成するときは、デリゲートとイベントを組み合わせて使用しました。基本的には、または何らかのポーリングでPropertyNotifyChanged
イベントを使用することがすべてです。WaitCallback
ConditionalWaiter
これが最善のアプローチであったかどうかはわかりませんが、うまくいきました。
先週のほとんどを大学の図書館で並行コードのデバッグについて勉強しました。中心的な問題は、並行コードが非決定的であることです。通常、アカデミック デバッグは次の 3 つの陣営のいずれかに分類されます。
上記のコメンテーターが気づいたように、並行システムをより決定論的な状態に設計できます。ただし、これを適切に行わないと、シーケンシャル システムの設計に戻るだけです。
私の提案は、何がスレッド化され、何がスレッド化されないかについて、非常に厳密な設計プロトコルを持つことに集中することです。要素間の依存関係が最小限になるようにインターフェイスを制約すると、はるかに簡単になります。
頑張って、問題に取り組み続けてください。
J2E コードについては、スレッドの同時実行テストに SilkPerformer、LoadRunner、および JMeter を使用しました。彼らは皆同じことをします。基本的に、TCP/IP データ ストリームを分析し、複数のユーザーがアプリ サーバーに同時にリクエストを行うことをシミュレートするために必要な、プロキシ サーバーのバージョンを管理するための比較的単純なインターフェイスを提供します。プロキシ サーバーを使用すると、要求の処理後に、サーバーに送信されたページ全体と URL、およびサーバーからの応答を提示することで、行われた要求の分析などを行うことができます。
セキュリティで保護されていない http モードでいくつかのバグを見つけることができます。このモードでは、少なくとも送信されているフォーム データを分析し、ユーザーごとに体系的に変更することができます。しかし、本当のテストは、https (Secured Socket Layers) で実行した場合です。次に、セッションと Cookie データを体系的に変更することにも対処する必要がありますが、これは少し複雑になる可能性があります。
並行性のテスト中に私が見つけた最大のバグは、開発者が Java ガベージ コレクションに依存して、ログイン時に確立された LDAP サーバーへの接続要求をログイン時に閉じることを発見したときでした。これにより、ユーザーが公開されました。サーバーがひざまずき、数秒ごとに1つのトランザクションをほとんど完了できなかったときに何が起こったのかを分析しようとすると、他のユーザーのセッションと非常に紛らわしい結果になりました。
最終的には、あなたまたは誰かが腰を落ち着けてコードを分析し、先ほど述べたような失敗を見つけなければならないでしょう。そして、上記の問題を明らかにしたときに起こったような、部門を超えたオープンな議論が最も役に立ちます。しかし、これらのツールは、マルチスレッド コードをテストするための最良のソリューションです。JMeter はオープン ソースです。SilkPerformer と LoadRunner は独自仕様です。アプリがスレッド セーフかどうかを本当に知りたい場合は、それが大手の方法です。私は専門的に非常に大企業のためにこれを行ってきたので、推測ではありません. 私は個人的な経験から話しています。
注意点: これらのツールを理解するには時間がかかります。マルチスレッド プログラミングに慣れていない限り、単にソフトウェアをインストールして GUI を起動するだけでは問題ありません。理解しなければならない領域の 3 つの重要なカテゴリ (フォーム、セッション、および Cookie データ) を特定しようとしました。ドキュメント全体。
(可能であれば) スレッドを使用せず、アクター / アクティブ オブジェクトを使用します。簡単にテストできます。
EasyMock.makeThreadSafe を使用して、テスト用インスタンスをスレッドセーフにすることができます