2017-10-03 6 views
-1

私は、JMSメッセージの受信を担当するWindowsサービスを持っています。私は実装の詳細の簡略版を提供しています。メッセージが到着すると、処理のために別のタスク(スレッド)に渡され、BlockingCollectionの助けを借りて最大数のタスクが制限されます。処理が成功し、各再試行間にいくらかの遅延量があるか、または最大再試行回数がなくなるまで、再試行するための再試行メカニズムが用意されています。再試行メカニズムの理由は、これらのメッセージを消費するレガシーアプリケーションの問題に対処するためです。レガシーシステムは、ペシミスティックロックを使用して構築され、メッセージの処理がエラーになることがあり、最終的には再試行がほとんど行われません。費用便益分析のために、レガシーシステムでの問題に対処しないことが決定されました。これらのアプリケーションは2〜3年後に置き換えられます。どのようにキャンセルすることができる遅延を実装するには?

この再試行メカニズムは、メッセージの処理を処理するのと同じタスクスレッドで実行されます。最初は、Thread.Sleepを使用して、各再試行の試行間に遅延を導入しました。それはうまくいったが、Windowsサービスをシャットダウンしようとすると、現在処理されているメッセージと再試行を待っているメッセージがあれば、時間がかかります。

次に、シャットダウンイベントが発生した場合に待機メカニズムをキャンセルする方法を実装する冒険に行きました。

2つの異なるアプローチを使用しました。

オプション#1 ManualResetEventを使用してワンと私は、私が代わりにコードを次している待たなければならないとき(投稿のみ関連するコード・ブロック)シャットダウンイベントが発生すると

private readonly ManualResetEvent _lockEvent = new ManualResetEvent(false); 

if (_lockEvent.WaitOne(TimeSpan.FromMilliseconds(120000))) 
{ 
    Log.Info($"Thread interrupted. Retrying will resume after windows service restarts for message id {messageId}"); 
    return; 
} 

が、私はcancellationTokenSourceキャンセルし、 ManualResetEventを設定します。すべて私が欲しいものをするように見えます。 CancellationTokenに依存するコードが正常にキャンセルされることを知って、再試行待ちを正常に中断するように、2つの操作を行う必要があります。

_subscriberCancellationTokenSource.Cancel(); 
_lockEvent.Set(); 

オプション#2 の.Net 4.6にアップグレードした後、私はどこに私ができるタスクタイプを使用して開始しました。私が実現し、私はここに、同様の遅延を実現するために、タスクを使用することができ、私は遅延を必要とする、これまで、私はちょうど

CancellationTokenを渡すことによって、メソッドを呼び出す

private void WaitBeforeRetrying(CancellationToken cancellationToken) 
{ 
    var waitingTask = Task.Delay(120000, cancellationToken); 
    waitingTask.Wait(cancellationToken); 
} 

をしようとしたコードの簡易版であります

WaitBeforeRetrying(SubscriberCancellationToken); 

シャットダウンイベントが発生すると、私はCancellationTokenSourceでキャンセルを呼び出すだけで、すべて正常にシャットダウンします。

_subscriberCancellationTokenSource.Cancel(); 

オプション1とオプション2の両方が仕事をしているようです。

オプション1に比べてオプション2に欠点がありますか?私がこれまで持っていたものよりも優れた選択肢?本当にすべての入力を感謝します。

UPDATE @EricLippertのコメントを読んだ後、私は間違っていたことを理解しました。私のスレッドのほとんどは、実際に生産的な作業をするのではなく、待機状態になっていました。これは、同期ワークフローで非同期コールをほとんど使用しなかった結果です。

private async Task WaitBeforeRetrying(CancellationToken cancellationToken) 
{ 
    await Task.Delay(120000, cancellationToken); 
} 

を次のように私は今、私の遅延方法を変更し、私は

await WaitBeforeRetrying(SubscriberCancellationToken); 

としてそれを呼び出すそして非同期メカニズムにトップ層へのすべての方法を伝播するために残りのコードをリファクタリング。私は待たなければならないだけでなく、スレッドがブロックされた状態にあるのを不必要に防ぐことができれば、遅延を簡単にキャンセルするのに役立ちました。本当にすべての人のフィードバックをいただきありがとうございます。

+5

私はダウンボートしませんでしたが、どちらのオプションも悪いです。あなたは非同期遅延を設定するすべての問題に行き、それから同期的に待機します!それは非常に奇妙なことです。そして、あなたはスレッドとタスクを融合させました。これは、「芝生を刈る」と言う芝生と、芝生を育てる人との間には違いがないということです。それらは非常に異なるものです。しかし悪いことに、あなたの場合、あなたの労働者に与えている仕事は「何もしない」ということです。あなたはそれのために労働者を雇う必要はありません! –

+0

@EricLippertありがとうございます。では、コールチェーン全体で非同期待機パターンを実装することを検討することを提案していますか? – Vinod

+2

あなたが非同期に待っている間に、他の作業を実行できるようにすることがポイントです。非同期メソッドによって生成されたタスクを同期的にブロックすることは、待っているポイント全体に対して機能しています。あなたが結果を同期的に待つつもりならば、なぜ非同期に待つのでしょうか?それは意味をなさない。しかし、それは悪化する... –

答えて

3

私は彼らが違うとは思わない。両方の効果は、時間がなくなるまでスレッドがブロックされることです。

ASP.NETでこれを使用している場合、スレッドをブロックすることは良いことではありません。その場合、メソッドasyncを作成し、await Task.Delayを使用することができます。遅延の後でコードを再開しますが、スレッドは他のものをその間に処理することができます。

private async Task WaitBeforeRetrying(CancellationToken cancellationToken) 
{ 
    await Task.Delay(120000, cancellationToken); 
} 
+0

@Gabriel Luciありがとう、あなただけでなく、ASP.NETで動作するだけでなく、プロジェクトの他のタイプの作品を提案した。 – Vinod

+0

真。限られた数のスレッドで実行されるため、ASP.NETのほうが重要です。 –

関連する問題