並列ループを使用してメッセージ キューからメッセージにアクセスするクラスを構築しています。この問題を説明するために、単純化されたバージョンのコードを作成しました。
public class Worker
{
private IMessageQueue mq;
public Worker(IMessageQueue mq)
{
this.mq = mq;
}
public int Concurrency
{
get
{
return 5;
}
}
public void DoWork()
{
int totalFoundMessage = 0;
do
{
// reset for every loop
totalFoundMessage = 0;
Parallel.For<int>(
0,
this.Concurrency,
() => 0,
(i, loopState, localState) =>
{
Message data = this.mq.GetFromMessageQueue("MessageQueueName");
if (data != null)
{
return localState + 1;
}
else
{
return localState + 0;
}
},
localState =>
{
Interlocked.Add(ref totalFoundMessage, localState);
});
}
while (totalFoundMessage >= this.Concurrency);
}
}
アイデアは、並列ループを制御するためにワーカー クラスに同時実行値を設定することです。各ループの後、メッセージ キューから取得するメッセージの数が同時実行数と等しい場合、キューにはさらに多くのメッセージがある可能性があると想定し、メッセージ数が同時実行数よりも小さくなるまでキューからフェッチし続けます。TPL コードは、 TPL Data Parallelism Issueの投稿にも触発されています。
メッセージキューとメッセージオブジェクトへのインターフェースがあります。
public interface IMessageQueue
{
Message GetFromMessageQueue(string queueName);
}
public class Message
{
}
したがって、単体テスト コードを作成し、Moq を使用してIMessageQueue
インターフェイスをモックしました。
[TestMethod()]
public void DoWorkTest()
{
Mock<IMessageQueue> mqMock = new Mock<IMessageQueue>();
Message data = new Message();
Worker w = new Worker(mqMock.Object);
int callCounter = 0;
int messageNumber = 11;
mqMock.Setup(x => x.GetFromMessageQueue("MessageQueueName")).Returns(() =>
{
callCounter++;
if (callCounter < messageNumber)
{
return data;
}
else
{
// simulate MSMQ's behavior last call to empty queue returns null
return (Message)null;
}
}
);
w.DoWork();
int expectedCallTimes = w.Concurrency * (messageNumber / w.Concurrency);
if (messageNumber % w.Concurrency > 0)
{
expectedCallTimes += w.Concurrency;
}
mqMock.Verify(x => x.GetFromMessageQueue("MessageQueueName"), Times.Exactly(expectedCallTimes));
}
Moqのアイデアを使用して、呼び出された時間に基づいて関数の戻り値を設定し、呼び出し時間に基づく応答を設定しました。
単体テスト中に、テスト結果が不安定であることに気付きました。複数回実行すると、ほとんどの場合、テストに合格することがわかりますが、さまざまな理由でテストに失敗することもあります。
何が原因で状況が発生したのかわかりません。あなたからの情報を探しています。ありがとう