2

MVC3 アプリケーションには、永久に実行することを意図した実行時間の長いスレッドがいくつかあります。

ThreadAbortException が他のコード (私のものではない) によって呼び出されているという問題が発生しており、この問題から正常に回復してスレッドを再起動する必要があります。現時点では、appDomain のワーカー プロセスをリサイクルするしかありませんが、これは理想とはほど遠いものです。

このコードの動作に関する詳細は次のとおりです。

この MVC3 アプリケーションには、シングルトン サービス クラスが存在します。データをキャッシュするため、シングルトンでなければなりません。このサービスは、データベースへのリクエストを行います。実際のデータベース接続コードには、サードパーティのライブラリが使用されます。

このシングルトン クラスでは、「QueryRequestors」と呼ばれるクラスのコレクションを使用します。これらのクラスは、データベースへの要求の一意の package+stored_procedure 名を識別して、それらの呼び出しをキューに入れることができるようにします。これが QueryRequestor クラスの目的です。同じ package+stored_procedure への呼び出し (無限に異なるパラメーターがある場合もあります) がキューに入れられ、同時に発生しないようにすることです。これにより、データベースの負担が大幅に軽減され、パフォーマンスが向上します。

QueryRequestor クラスは、内部 BlockingCollection と内部 Task (スレッド) を使用して、そのキュー (ブロッキング コレクション) を監視します。リクエストがシングルトン サービスに入ると、package+stored_procedure 名を介して正しい QueryRequestor クラスを見つけ、そのクラスにクエリを渡します。クエリはキューに入れられます (ブロッキング コレクション)。QueryRequestor の Task は、キューにリクエストがあることを確認し、データベースを呼び出します (現在、サード パーティのライブラリが関与しています)。結果が戻ってくると、シングルトン サービスにキャッシュされます。Task は、ブロッキング コレクションが空になるまで要求の処理を続行し、その後待機します。

QueryRequestor が作成されて実行されると、それが死ぬことは決してありません。このサービスには 24 時間 365 日、数分ごとにリクエストが届きます。サービスのキャッシュにデータがある場合は、それを使用します。データが古くなると、次のリクエストがキューに入れられます (そして、後続の同時リクエストは、誰か (別のスレッド) がキューに入れられたリクエストを既に作成していることを知っているため、キャッシュを使用し続けます。これは効率的です)。

ここでの問題は、QueryRequestor クラス内の Task が ThreadAbortException に遭遇したときに何をすべきかということです。理想的には、そこから回復してスレッドを再開したいと思います。または、少なくとも、QueryRequestor を破棄して (私に関する限り、現在は「壊れた」状態にあります)、最初からやり直してください。package+stored_procedure 名に一致する次のリクエストで、サービスに存在しない場合は新しい QueryRequestor が作成されるためです。

スレッドがサードパーティのライブラリによって強制終了されているのではないかと思いますが、確信が持てません。私が知っているのは、スレッド/タスク中止したり、強制終了しようとしたりする場所がないということだけです。ずっと走らせたい。しかし、明らかに、この例外のためのコードを用意する必要があります。スレッドが中止されたためにサービスが爆発するのは非常に面倒です。

これを処理する最善の方法は何ですか? どうすればこれを優雅に処理できますか?

4

2 に答える 2

2

Thread.ResetAbortThreadAbortExceptionを呼び出すことで、の再スローを停止できます。

例外の最も一般的なケースはリダイレクト呼び出しであり、スレッドの中止をキャンセルすると、スレッドを強制終了するために無視される要求コードの実行の望ましくない影響が発生する可能性があることに注意してください。これは、MVC (コントローラーから特別なリダイレクト結果を返すことができる) よりも WinForms (コードとレンダリングの分離が明確でない) でよくある問題です。

于 2013-03-27T18:02:19.263 に答える
1

これが私が解決策として思いついたもので、非常にうまく機能します。

ここでの本当の問題は、ThreadAbortException を防止することではありません。とにかく防止することはできず、防止したくないからです。これが発生したことを示すエラー レポートが表示されるのは、実際には良いことです。それが原因でアプリがダウンすることを望んでいません。

したがって、私たちが本当に必要としていたのは、アプリケーションをダウンさせることなく、この例外を適切に処理する方法でした。

私が思いついた解決策は、「IsValid」と呼ばれる QueryRequestor クラスに bool フラグ プロパティを作成することでした。このプロパティは、クラスのコンストラクターで true に設定されます。

QueryRequestor クラスの別のスレッドで実行される DoWork() 呼び出しで、ThreadAbortException をキャッチし、このフラグを FALSE に設定します。これで、このクラスが無効な (破損した) 状態にあり、使用しないことを他のコードに伝えることができます。

これで、この QueryRequestor クラスを利用するシングルトン サービスは、この IsValid プロパティをチェックすることを認識します。有効でない場合は、QueryRequestor を新しいものに置き換え、人生は続きます。アプリケーションはクラッシュせず、壊れた QueryRequestor は破棄され、ジョブを実行できる新しいバージョンに置き換えられます。

テストでは、これは非常にうまく機能しました。DoWork() スレッドで意図的に Thread.Abort() を呼び出し、デバッグ ウィンドウで出力行を確認します。アプリは、スレッドが中止されたことを報告し、シングルトン サービスが QueryRequestor を正しく置き換えていました。後任は、要求を正常に処理することができました。

于 2013-03-27T19:21:17.817 に答える