沸騰冷水

煮え滾った熱い思いを氷のように冷たく見つめなおしたい

ASP.NET VB.NET ThreadAbortExceptionについて

ASP.NETのHttpResponse.End()を実行すると、.NETの例外ThreadAbortExceptionが発生する。
MSDNには特殊な例外と書いてある。なんだ特殊って。例外自体が特殊という認識なんですが。。。

ThreadAbortException(MSDN):
スレッドを破棄するために Abort メソッドが呼び出されるときに、共通言語ランタイムは ThreadAbortException をスローします。ThreadAbortExceptionは、キャッチできても、catch ブロックの末尾でもう一度自動的に発生する特殊な例外です。この例外が発生すると、ランタイムは、スレッドを終了する前に finally ブロックをすべて実行します。スレッドは、finally ブロックで無制限に計算を実行することも、Thread.ResetAbort を呼び出して中止をキャンセルすることもできるため、スレッドが終了するかどうかは保証されません。中止されたスレッドが終了するまで待機する場合は、Thread.Join メソッドを呼び出すことができます。Join は、スレッドが実際に実行を停止するまで戻らないブロッキング呼び出しです。

http://msdn.microsoft.com/ja-jp/library/system.threading.threadabortexception(VS.80).aspx

このHttpResponse.End()の内部でAbortが実行されているということですかね。
調べてみると、このThreadAbortExceptionが実行されても、処理は継続されている模様。
意図的ではなく発生しているAbort以外は実のところエラーではないということでしょうか。
Abortしたよ!ってのをわざわざ例外発生して知らせてくれているということですね。

この問題が発生している社内システムでは、例外は全てキャッチして、イベントビューワに内容を出力しておき、ユーザー打上げのみならず、開発側から積極的に分析できるようにしてある。
そのためこのThreadAbortExceptionが発生する度、イベントビューワに例外が起きたという出力が残ってしまうのだ。

ユーザーの端末にはエラーが見えていないにも関わらず、開発側の私としてエラーが発生しているように見える。うーん、普段は逆パターンになるんだけどなぁ。

対策としていろいろ議論されているが

  • 無視する
  • HttpResponse.End()を呼び出さない

の2つで対策を検討。

1:無視する

1では、イベントビューワに書き出さないという方向で検討した。
例外オブジェクトを作成するとき、ThreadAbortExceptionの場合は処理しないという作りか、発生しているファンクションのTry〜Catchで意図的に取得し、処理は特に何もしない、という2パターン。
前者を行うのは、これ以外でThreadAbortExceptionが発生したときに検知できなくなるため、断念。
後者で行こうと思ったのだが、この例外はなんとCatchした後、Catchの末尾でもう一度発生するらしい。
この例外が発生する関数の呼び出し側にTry〜Catchが書かれていると、そちらにもThreadAbortExceptionが発生してしまうのだ。
これでは呼び出し側の親へ親へ同じ記述が増えてしまう。

2:HttpResponse.End()を呼び出さない

そこで2の案を考えた。
HttpResponse.End()を呼ばない仕組みに変更できないかということ。
処理内容としては、データをCSV形式にしてユーザーへ送信する。というものなので、代替手段として、HttpResponse.Flush()→HttpResponse.Close()に置換えてみる。
HttpResponse.End()と、この関数の大きな違いは、最終的に呼び出されるEndRequestイベントがHttpReponse.Close()では発生しないようだ。

このイベントというのは

EndRequestイベント
ASP.NET が要求に応答するときに、実行の HTTP パイプライン チェインの最後のイベントとして発生します。

http://msdn.microsoft.com/ja-jp/library/system.web.httpapplication.endrequest.aspx

とある。

しかしHttpResponse.Close()もクライアントとのリクエストを閉じるのため、内部的に同じイベントが発生するんじゃないのか?と少し疑問。そこについては深く調べなかった。

この代替案にてテストを実施すると、結果の出力はもちろん変わることがなく、さらに例外も発生せずに処理が終了できた。