3

マルチスレッド アプリケーションを実行すると NullReferenceException が発生しますが、これはデバッガーの外部でリリース モードで実行した場合に限られます。スタック トレースはログに記録され、常に同じ関数呼び出しを指します。関数にログ ステートメントをいくつか入れて、どれだけ到達するかを判断しようとしました。関数の最後の行にあるステートメントを含め、すべてのステートメントがログに記録されます。興味深いのは、NullReferenceException が発生すると、関数呼び出しの後のステートメントがログに記録されないことです。

    // ...
    logger.Log( "one" );  // logged
    Update( false );
    logger.Log( "eleven" );  // not logged when exception occurs
}

private void Update( bool condition )
{
    logger.Log( "one" );  // logged
    // ...  
    logger.Log( "ten" );  // logged, even when exception occurs
}

関数が呼び出されるたびに例外が発生するわけではありません。関数の実行前または実行中にスタックが破損して、戻りアドレスが失われ、null 参照が発生する可能性はありますか? .NET でそのようなことが可能だとは思いませんでしたが、もっと奇妙なことが起こったと思います。

関数の呼び出しを関数の内容に置き換えてみたので、すべてがインラインで発生し、次のような行で例外が発生します。

foreach ( ClassItem item in classItemCollection )

「classItemCollection」がnullではないことをログで確認しました。また、IEnumeratorが何かおかしいことをしている場合に備えて、foreachをforに変更しようとしましたが、同じ行で例外が発生しました。

これをさらに調査する方法についてのアイデアはありますか?

更新:何人かのレスポンダーが、ロガーが null でないことを確認することに関する可能な解決策を提案しています。明確にするために、ログ ステートメントは、例外が発生し始めた後にデバッグ目的で追加されました。

4

6 に答える 6

5

null 参照が見つかりました。Fredrik と micahtan が示唆したように、私はコミュニティが解決策を見つけるのに十分な情報を提供していませんでした。

これは、何が起こっていたかを表しています。

ISomething something = null;

//...

// the Add method returns a strong reference to an ISomething
// that it creates.  m_object holds a weak reference, so when
// "this" no longer has a strong reference, the ISomething can
// be garbage collected.
something = m_object.Add( index );

// the Update method looks at the ISomethings held by m_object.
// it obtains strong references to any that have been added,
// and puts them in m_collection;
Update( false );

// m_collection should hold the strong reference created by 
// the Update method.
// the null reference exception occurred here
something = m_collection[ index ];

return something;

問題は、Update メソッドが永続的な参照を取得するまで、「something」変数を一時的な強い参照として使用したことであることが判明しました。リリース モードのコンパイラは、"something = m_object.Add();" を最適化します。「何か」は再度割り当てられるまで使用されないためです。これにより、ISomething をガベージ コレクションできるようになり、アクセスしようとしたときに m_collection に存在しなくなりました。

私がしなければならなかったのは、Update の呼び出しが終わるまで強力な参照を保持していることを確認することだけでした。

これが誰にとっても役立つかどうかは疑わしいですが、もし誰かが興味を持っている場合のために、私はこの質問に答えずに残したくありませんでした.

于 2009-06-11T21:00:35.110 に答える
2

「10」をログに記録するという事実は、最初に次のことを確認します。

  • これまでに割り当てられています...これはloggerおそらく何らかの形でnullになっていますか
  • Logそれ自体のバグです

どちらも十分な文脈がないとわかりにくいですが、それが私が調査する方法です. null簡単なテストをどこかに追加することもできます。生意気なアプローチとして、メソッドの名前を別の名前に変更しLog、拡張メソッドを追加できます。

[Conditional("TRACE")]
public static void Log(this YourLoggerType logger, string message) {
    if(logger==null) {
       throw new ArgumentNullException("logger",
            "logger was null, logging " + message);
    } else {
       try {
           logger.LogCore(message); // the old method
       } catch (Exception ex) {
           throw new InvalidOperationException(
                "logger failed, logging " + message, ex);
       }
    }
}

既存のコードは新しいLog拡張メソッドを呼び出す必要があり、例外によって、バーフィードした正確な場所が明確になります。修正したら元に戻すか、そのままにしておくかもしれません。

于 2009-06-10T22:15:44.603 に答える
0

複数のスレッドから classItemCollection を変更していますか? 別のスレッドでコレクションを変更すると、イテレータが無効になり、例外が発生する可能性があります。ロックでアクセスを保護する必要がある場合があります。

編集: ClassItem と classItemCollection のタイプに関する詳細情報を投稿できますか?

もう 1 つの可能性は、ClassItem が値型であり、classItemCollection がジェネリック コレクションであり、どういうわけかコレクションに null が追加されていることです。以下は NullReferenceException をスローします。

        ArrayList list=new ArrayList();

        list.Add(1);
        list.Add(2);
        list.Add(null);
        list.Add(4);

        foreach (int i in list)
        {
            System.Diagnostics.Debug.WriteLine(i);
        }

この特定の問題は、int によって解決できますか? i またはオブジェクト i foreach または汎用コンテナーを使用します。

于 2009-06-10T22:26:59.260 に答える
0

Fredrik に同意します。詳細が必要です。おそらく探し始める1つの場所:マルチスレッドアプリケーションと、リリースで発生しているがデバッグではないエラーについて言及しています。複数のスレッドが同じオブジェクト参照にアクセスすると、タイミングの問題が発生する可能性があります。

とにかく、私はおそらく次のものも入れます:

Debug.Assert(classItemCollection != null);

ループ反復の直前。リリース モードでは役に立ちませんが、問題が発生した場合 (いつ?) デバッグで問題を特定するのに役立つ場合があります。

于 2009-06-10T22:27:30.740 に答える
0

ロガーまたはその依存関係の 1 つを null に設定しているコードを探します。null に設定すると、これをトリガーする可能性のあるロガーのプロパティはありますか? リリース モードはアプリケーションの実行を高速化することがあり、デバッグ モードやデバッガーのパフォーマンス ペナルティによって隠されている同期の問題を明らかにすることがあります。

于 2009-06-10T22:30:46.913 に答える
0

「eleven」がログに記録されていないという事実は、その呼び出しが行われる直前にロガーが null に設定されていると私に信じさせます。それを try/catch でラップして、ブロックの catch 部分にヒットするかどうかを確認できますか? おそらく、MessageBox.Show を挿入するか、既知のファイルに何かを書き込むことができます。

于 2009-06-10T22:36:41.690 に答える