29

「C# 4.0 in a Nutshell」(O'Reilly) を読み終えたところですが、C# に切り替えようとしているプログラマーにとっては素晴らしい本だと思いますが、疑問が残りました。using私の問題は、ステートメントの定義です。本(p.138)によると、

using (StreamReader reader = File.OpenText("file.txt")) {
    ...
}

以下と正確に同等です:

StreamReader reader = File.OpenText("file.txt");
try {
    ...
} finally {
    if (reader != null)
        ((IDisposable)reader).Dispose();
}

ただし、これが正しく、このコードが別のスレッドで実行されるとします。このスレッドは現在 で中止されてthread.Abort()いるため、 aThreadAbortExceptionがスローされ、スレッドがリーダーを初期化した直後でtry..finally句に入る前にあるとします。これは、リーダーが処分されていないことを意味します。

考えられる解決策は、次のようにコーディングすることです。

StreamReader reader = null;
try {
    reader = File.OpenText("file.txt");
    ...
} finally {
    if (reader != null)
        ((IDisposable)reader).Dispose();
}

これはアボートセーフです。

今私の質問のために:

  1. 本の著者は正しく、usingステートメントはアボートセーフではありませんか、それとも間違っていて、私の2番目のソリューションのように動作しますか?
  2. が最初のバリアント (アボートセーフではない) と同等である場合using、なぜチェックインするnullfinallyですか?
  3. 本 (p. 856) によると、ThreadAbortExceptionマネージド コードのどこにでもスローできます。しかし、おそらく例外があり、最初の亜種はアボートセーフなのでしょうか?

編集:thread.Abort()使用は良い習慣とは見なされないことを知っています。using私の関心は純粋に理論的なものです。ステートメントは正確にどのように動作するのでしょうか?

4

9 に答える 9

18

この本のコンパニオンWebサイトには、スレッドの中止に関する詳細がここにあります

要するに、最初の翻訳は正しいです(ILを見ればわかります)。

2番目の質問に対する答えは、変数が正当にnullになる可能性があるシナリオがある可能性があるということです。たとえば、GetFoo()はここでnullを返す場合があります。この場合、暗黙のfinallyブロックでNullReferenceExceptionがスローされることは望ましくありません。

using (var x = GetFoo())
{
   ...
}

3番目の質問に答えるために、Abortを安全にする唯一の方法(フレームワークコードを呼び出している場合)は、後でAppDomainを破棄することです。これは実際には多くの場合実用的な解決策です(実行中のクエリをキャンセルするたびにLINQPadが実行するのとまったく同じです)。

于 2010-10-13T13:57:57.730 に答える
9

2 つのシナリオに違いはありません。2 番目のシナリオでは、OpenText の呼び出し後、結果がリーダーに割り当てられる前に ThreadAbort が発生する可能性があります。

基本的に、ThreadAbortException を取得すると、すべての賭けが無効になります。そのため、スレッドを正常に終了させる他の方法を使用するのではなく、意図的にスレッドを中止しないでください。

あなたの編集に応えて -- あなたの 2 つのシナリオは実際には同一であることを再度指摘します。File.OpenText 呼び出しが正常に完了して値を返さない限り、'reader' 変数は null になるため、最初の方法と 2 番目の方法でコードを書き出す間に違いはありません。

于 2010-10-13T12:18:28.253 に答える
7

Thread.Abort非常に悪いジュジュです。人々があなたがすでに多くの問題に直面していると電話している場合 (回復不能なロックなど)。Thread.Abort実際には、病気のプロセスを侵すスキャンニオに限定する必要があります。

通常、例外は問題なくアンロールされますが、極端な場合、すべてのコードが実行できるという保証はありません。より差し迫った例は、「停電したらどうなるか?」です。

小切手を再null確認してください。File.OpenText返された場合はどうなりnullますか?OK、そうはなりませんが、コンパイラはそれを知りません。

于 2010-10-13T12:17:26.747 に答える
4

少し話が逸れますが、スレッドの中断中の lock ステートメントの動作も興味深いものです。lock は次と同等ですが:

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
    …
}
finally {
    System.Threading.Monitor.Exit(obj);
}

Monitor.Enter と try ステートメントの間でスレッド アボートが発生しないことが (x86 JITter によって) 保証されています。
http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx

生成された IL コードは .net 4 では異なるようです:
http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx

于 2010-10-13T12:38:37.237 に答える
2

本の著者は正しく、usingステートメントは中絶安全ではありませんか、それとも間違っていて、私の2番目の解決策のように動作しますか?

この本(p。856)によると、ThreadAbortExceptionはマネージコードのどこにでもスローできます。しかし、おそらく例外があり、最初のバリアントは結局中絶に対して安全ですか?

著者は正しいです。usingブロックはアボートセーフではありません。2番目のソリューションもアボートセーフではありません。リソース取得の途中でスレッドがアボートされる可能性があります。

アボートセーフではありませんが、リソースが管理されていないディスポーザブルは、ファイナライザーも実装する必要があります。ファイナライザーは、最終的に実行され、リソースをクリーンアップします。ファイナライザーは、リソース取得の途中でスレッドが異常終了した場合に備えて、完全に初期化されていないオブジェクトも処理できるように十分に堅牢である必要があります。

AThread.Abortは、制約付き実行領域(CER)、finallyブロック、catchブロック、静的コンストラクター、およびアンマネージコード内で実行されているコードのみを待機します。したがって、これはアボートセーフなソリューションです(リソースの取得と廃棄に関してのみ)。

StreamReader reader = null;
try {
  try { }
  finally { reader = File.OpenText("file.txt"); }
  // ...
}
finally {
  if (reader != null) reader.Dispose();
}

ただし、注意してください。アボートセーフコードは、ブロックではなく高速で実行する必要があります。アプリドメインのアンロード操作全体がハングする可能性があります。

使用が最初のバリアント(アボートセーフではない)と同等である場合、なぜ最終的にnullをチェックするのですか?

nullをチェックするとusing、参照が存在する場合にパターンが安全になりnullます。

于 2011-03-02T18:07:57.213 に答える
2

言語仕様は、最初のものが正しいと明確に述べています。

http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx MS Spec(Word文書)
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf ECMA仕様

スレッドが中止された場合、両方のコード バリアントが失敗する可能性があります。式が評価された後、ローカル変数への代入が発生する前に中止が発生した場合の 2 つ目。

ただし、アプリドメインの状態を簡単に破損する可能性があるため、スレッドの中止は使用しないでください。したがって、アプリドメインを強制的にアンロードする場合にのみ、スレッドを中止してください。

于 2010-10-13T12:27:08.290 に答える
2

あなたは間違った問題に焦点を合わせています。ThreadAbortException は、OpenText() メソッドを中止する可能性があります。それに対して回復力があることを期待するかもしれませんが、そうではありません。フレームワーク メソッドには、スレッド アボートを処理しようとする try/catch 句がありません。

ファイルは永久に開かれたままではないことに注意してください。FileStream ファイナライザは、最終的にファイル ハンドルを閉じます。もちろん、実行を続けてファイナライザーが実行される前にファイルを再度開こうとすると、プログラムで例外が発生する可能性があります。ただし、これは、マルチタスク オペレーティング システムを実行している場合は、常に防御する必要があることです。

于 2010-10-13T12:44:52.727 に答える
0

前者は実際、後者とまったく同じです。

すでに指摘したように、ThreadAbort は確かに悪いことですが、タスク マネージャーでタスクを強制終了したり、PC の電源をオフにしたりすることとはまったく同じではありません。

ThreadAbort はマネージ例外であり、可能な場合にのみランタイムによって発生します。

とはいえ、ThreadAbort に慣れたら、わざわざクリーンアップを試みる必要はありません。とにかくあなたは死の苦しみにいます。

于 2010-10-13T12:39:47.297 に答える
-2

最終ステートメントは常に実行されます。MSDNによると、「finally は、前の try ブロックがどのように終了したかに関係なく、コードのステートメント ブロックの実行を保証するために使用されます」。

したがって、リソースなどをクリーンアップしないことを心配する必要はありません (ウィンドウ、Framework-Runtime、または制御できないその他の何かが発生した場合のみですが、リソースのクリーンアップよりも大きな問題があります ;-))

于 2010-10-13T13:26:07.703 に答える