.NET 非同期 MSMQ
-
14-11-2019 - |
質問
これのどこが間違っているのかわかりません。基本的に、メッセージキューから受信してメッセージを処理するプログラムがあります。プログラムはいつでも停止できます。その場合、プログラムが終了する前にメッセージ ループが実行中の処理を終了します。次のコードでこれを実現しようとしています。
private MessageQueue q;
private ManualResetEventSlim idle;
public void Start()
{
idle = new ManualResetEventSlim();
q.ReceiveCompleted += this.MessageQueue_ReceiveCompleted;
q.BeginReceive();
}
public void Stop()
{
this.q.Dispose();
this.idle.Wait();
}
private void MessageQueue_ReceiveCompleted(object sender,
ReceiveCompletedEventArgs e)
{
Message inMsg;
try
{
inMsg = e.Message;
}
catch (Exception ex)
{
this.idle.Set();
return;
}
// Handle message
this.q.BeginReceive();
}
明らかなように、Stop メソッドはメッセージ キューを破棄し、アイドル待機ハンドルが設定されるのを待ちます (これは、破棄時に ReceiveCompleted イベントが呼び出されるために発生するはずですが、e.Message プロパティは例外とする必要があります)。
ただし、メッセージのループは続くだけです。メッセージ キューを破棄しましたが、メッセージ キューからの読み取りは引き続き行われ、例外ハンドラーは呼び出されません。つまり、idle.Wait 行が永久に待機することになります。
私の理解では、メッセージ キューを破棄すると保留中の受信を終了してイベントを呼び出す必要がありますが、e.Message (または q.EndReceive) は例外をスローする必要があります。そうではありませんか?そうでない場合、メッセージ ループを安全に終了するにはどうすればよいでしょうか?
ありがとう
アップデート:
完全な例を次に示します (キューが存在すると仮定します)。
class Program
{
static MessageQueue mq;
static ManualResetEventSlim idleWH;
static void Main(string[] args)
{
idleWH = new ManualResetEventSlim();
Console.WriteLine("Opening...");
using (mq = new MessageQueue(@".\private$\test"))
{
mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(int) });
mq.ReceiveCompleted += mq_ReceiveCompleted;
for (int i = 0; i < 10000; ++i)
mq.Send(i);
Console.WriteLine("Begin Receive...");
mq.BeginReceive();
Console.WriteLine("Press ENTER to exit loop");
Console.ReadLine();
Console.WriteLine("Closing...");
mq.Close();
}
Console.WriteLine("Waiting...");
idleWH.Wait();
Console.WriteLine("Press ENTER (ex)");
//Console.ReadLine();
}
static void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
try
{
var msg = mq.EndReceive(e.AsyncResult);
Console.Title = msg.Body.ToString();
// Receive next message
mq.BeginReceive();
}
catch (Exception ex)
{
idleWH.Set();
return;
}
}
}
解決 2
これを機能させる唯一の方法は、トランザクション キューを使用することでした。非トランザクション キューはこれに対して脆弱であるようです。答えではありませんが、これを見つけた人に私ができる最高のアドバイスです。
他のヒント
どのようにしてこれを機能させたのかまったくわかりません。あなた しなければならない イベント内で MessageQueue.EndReceive() を呼び出します。そのメソッドのみが例外をスローできます。ReceiveCompleted イベントの MSDN サンプル コードを確認してください。また、例外をキャッチしないでください。診断できないエラーが発生するだけです。キューを破棄するときに取得する特定の例外、ObjectDisusedException をキャッチします。
private static volatile bool _shouldStop = false;
. . .
_shouldStop = true;
mq.Close();
. . .
try
{
var msg = mq.EndReceive(e.AsyncResult);
if ( _shouldStop)
{
idleWH.Set();
return;
}
mq.BeginReceive();
}
. . .