4

私はコードの例外安全性の改善に取り組んでおり、C#コンストラクトThreadAbortExceptionでリソースを保護している場合でも、raise によって望ましくないリソース リークが発生する可能性があることに気付きました。usingたとえば、次のコードを考えてみましょう (別のスレッドで実行されている可能性があります)。

using (TextWriter writer = CreateWriter(filename))
{
    // do something with the writer.
}

TextWriter CreateWriter(string filename)
{
    return new CustomWriter(File.OpenWrite(filename));
}

このコードを実行しているスレッドが異常終了した場合は、 が参照するファイル ハンドルfilenameをすぐに閉じてください。usingコンストラクトの使用を try/finally ブロックに置き換えずにこれを行うことはできますか?

これは、いつでも発生する可能性があるということです。つまりThreadAbortException、ステートメント間で何が起こっているかに注意を払う必要があります。CreateWritertry/finally ブロックを使用して例外を防ぐことはできますが、括弧内の式が評価されるまで構造は同じことを行いません。つまり、リターンusingの直後に例外が発生した場合、ファイル リソースは開いたままになります。CreateWriter

ThreadAbortExceptionファイナライザーが最終的にファイルハンドルを解放することは理解していますが、使用されている各場所でキャッチせずにこの問題に対処する決定論的な方法があるかどうか疑問に思っていますCreateWriter.

4

4 に答える 4

4

はい、これを防ぐ決定論的な方法は、を使用しないことThread.Abortです。これまで。停止する時間になったことをスレッドに通知し、正常に終了させます。Thread.AbortあなたをつまずかせるためだけにAPIに配置された、非常に大きな赤いニシンです。;)

http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation

于 2011-06-06T18:03:04.330 に答える
1

トレードオフがあります。

  1. ThreadAbortExceptionが存在する場合でも、必ずすべてのリソースをすぐに閉じてください
  2. コードは単純ですが、Abort()が呼び出されると一時的にリソースがリークします

私はあなたが中絶を呼んでいないと思います、そして他の誰かがそうするならばただ安全である方法を望んでいます。Abortに電話する場合は、電話しないことをお勧めします。遭遇する問題はこれだけではありません。ドキュメントの中止には他にも問題があります。

#2は、Abort()の呼び出し元がこれを予期する必要があるため、有効な選択です。

#1を選択したい場合は、単純なtry/catchでも役に立たないと思います。ThreadAbortExceptionがどこでも発生する可能性がある場合は、ファイルが開かれた後(File.OpenWrite()内)で、Dispose()を呼び出すことができる変数にファイルを割り当てる前に発生する可能性があります-同じ問題が発生しますコードで使用するように。

次のようなセマンティクスが必要です

  using (var handle = GetUnOpenedHandle()) {
        handle.Open(); // this can't involve assignment to any field of handle
  }

これが可能かどうかはわかりません。

于 2011-06-06T18:16:35.570 に答える
1

多くの場合 (すべてではありませんが)、. ThreadAbortException.NET BCL の重要なコードのほとんどは、すでにこれをかなりうまく行っています。問題は、正しくするのが本当に難しいということです。このため、ほとんどの人は、スレッドの中止を避けることをお勧めします。バージョン 2.0 以降、CLR はスレッドの中止をより許容できるものにし、コード作成者がそれらを防ぐのに役立つ新しい API セットを導入しました。このすべてがどのように機能するかの詳細については、Constrained Execution Regionsをご覧ください。

usingブロックの例に対するあなたの懸念は正しいと思います。制約付きの実行領域が正しく機能するには、帯域外 (非同期) 例外がtryブロック内から発生する必要があります。ただし、using展開方法により、式はtryブロックの外側で評価されます。lockブロック内から式を評価するブロックの展開と比較してtryください。とにかく、それはフレームワークのバージョン 4.0 に当てはまり、これらの例外を防ぐために特別に変更されました。

usingしたがって、問題は、ブロックで同じ変更が行われなかった理由です。Joe Duffy によると、これは許容できる省略でした。なぜなら、スレッドのアボートの後には常にAppDomain の終了が続き、いずれにせよファイナライザーが起動されるはずだからです。

あ、はい。あなたのコードは帯域外 (非同期) 例外に対して寛容ではありません。しかし、私よりも賢い人たちの一般的な知恵は、そうである必要はないということです.

于 2011-06-06T18:37:27.583 に答える
0

致命的なエラーが発生した場合、スレッドアボートが最も頻繁に使用されるため、アプリケーションを終了させることが応答の可能性があります。自分のスレッドをきれいに停止しようとしている場合は、Thread.Join()を使用してください。

于 2011-06-06T18:16:02.467 に答える