2

とすれば:

  • ファイナライザーでロックを取得すると、デッドロックが発生する可能性があります
  • ファイナライザーは例外をスローできます

未処理の例外ハンドラー内でロックを取得しても安全ですか、それとも以下のコードでデッドロックが発生する可能性がありますか?

static void Main(string[] args)
{
     AppDomain.CurrentDomain.UnhandledException += 
         new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
     //do other stuff
}

private static object loggingLock = new object();

static void CurrentDomain_UnhandledException(
    object sender, 
    UnhandledExceptionEventArgs e)
{
    lock (loggingLock)
    {
        //log the exception
    }
}
4

3 に答える 3

3

とすれば:

  • ファイナライザーでロックを取得すると、デッドロックが発生する可能性があります
  • ファイナライザーは例外をスローできます

編集結局のところ、ファイナライザーでスローされた例外は、定義上致命的です。

doc: Finalize または Finalize のオーバーライドが例外をスローし、デフォルト ポリシーをオーバーライドするアプリケーションによってランタイムがホストされていない場合、ランタイムはプロセスを終了し、アクティブな try-finally ブロックまたはファイナライザーは実行されません。ファイナライザがリソースを解放または破棄できない場合、この動作によりプロセスの整合性が保証されます。

c#ファイナライザーが例外をスローするも参照してください。

: 例外が関数内で発生した可能性がありますが、これはその関数のコンテキストで処理されるという意味ではありません。実際、スタックは巻き戻されていたはずです。

doc: 適用可能な例外ハンドラーが見つからずにスレッドのスタック全体がアンワインドされた場合にのみ、例外が処理されないため、イベントが発生する最初の場所は、スレッドが発生したアプリケーション ドメインです。


ロックするのが安全ではない理由がわかりません。(通常の警告が適用されます: ロックを保持している間はブロック操作を行わないでください...)。

ただし、ここで再入可能性と無限再帰についてよく考えてください。

  • ロギング中のエラーにどのように対応しますか? スレッドがすでにロックを保持しているため、ロックは定義によって取得されます。しかし、ロギング コードは再入可能ですか? つまり、別のログ操作を呼び出すと、進行中の (失敗/失敗した) 操作の状態が台無しになりますか? ロギングは可能でしょうか?

    --> 再入可能性が許可されていない場合 (または特別なアクション (他の場所でログを記録するなど) が必要な場合) は、ロックが取得されていても、明示的な 'inLoggingOperation' フラグが必要です。

  • 重要な点: ロギングが完全に例外に対応していない場合、既に CurrentDomain.UnhandledException にあるときに問題が発生する可能性があります (ドキュメントには、イベント ハンドラーで例外が発生したときに何が起こるかは記載されていません)。

于 2012-09-27T21:43:45.653 に答える
0

さて、私はいくつかのハンティングを行い、MSDN からこれを見つけました。

フォームのロックステートメント

lock (x) ...

wherexは参照型の式であり、正確に同等です

System.Threading.Monitor.Enter(x);
try {
   ...
}
finally {
   System.Threading.Monitor.Exit(x);
}

ただし、x評価は 1 回だけです。

相互排他ロックが保持されている間、同じ実行スレッドで実行されているコードは、ロックを取得および解放することもできます。ただし、他のスレッドで実行されているコードは、ロックが解放されるまでブロックされてロックを取得できません。

8.12 ロック文

したがって、finallyステートメントが原因でロック内から例外がスローされた場合、ロックは解放されます。

その情報があれば、メソッド内からロックしようとしてデッドロックしないことを 95% 確信できますCurrentDomain_UnhandledException。誰かが別の方法で知っているなら、私は彼らからここに来たいです(そして参考文献もいいでしょう).

于 2012-09-27T21:43:19.460 に答える
0

後世のために...いくつかのテストコード:

class Program
{
    static AutoResetEvent e1 = new AutoResetEvent(false);
    static AutoResetEvent e2 = new AutoResetEvent(false);
    private static object lockObject = new object();

    private static void threadProc()
    {
        lock (lockObject)
        {
            e1.Set();
            e2.WaitOne();
            Console.WriteLine("got event");
        }
    }

    private static int finalized = 0;

    public class finalizerTest
    {

        ~finalizerTest()
        {
            try
            {
                throw new NullReferenceException();
            }
            finally
            {
                Interlocked.Increment(ref finalized);
            }
        }
    }

    static void Main(string[] args)
    {
        ThreadPool.QueueUserWorkItem((a) => threadProc());
        e1.WaitOne();
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        {
            finalizerTest f = new finalizerTest();
        }

        //uncommenting this will cause logging to happen as expected
        /*
        while (finalized == 0)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
         */

    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine("in handler -- pre lock");
        e2.Set();

        lock (lockObject)
        {
            Console.WriteLine("in handler");
        }
    }
}

アプリケーションがメインを離れたために finalizerTest がファイナライズされた場合、出力は次のようになります。

in handler -- pre lock

ただし、GC.Collect/WaitForPending ファイナライザーのためにファイナライズされている場合は、次のようになります。

in handler -- pre lock
got event
in handler

これが意味することは、アプリケーションのダウン中にファイナライザーからスローされた例外の特定のケースでは、ロックを取得できない可能性があるということですが、この場合、アプリケーションとファイナライズ キューはすでに深刻な問題に陥っており、ロックは行われていません。悪化させます。

他のすべてのテストでは、すべてが期待どおりに発生し、threadProcウェイクアップし、ログが記録されると考えることができました。

于 2012-09-28T05:20:34.683 に答える