0

わかりましたので、このコード行で非常に散発的に NullReferenceException を取得します。

if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)

および/またはこの行:

_oraclePlanSettings = _oracleTenantSettings.OraclePlanSettings[_key];

ここで、OraclePlanSettings は SortedList であり、問​​題のコードが以下で囲まれているため、null にすることはできません。

 if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0)

だから私はNREを取得していますが、コード行全体の単一の部分がnullになる可能性はありません。限目。(フラストレーションを感じますか?)それにはキーが含まれますが、とにかくNREをスローしません。理解できません。VS が CLR 例外を誤って配置している可能性はありますか? もしそうなら、どこから探し始めるのが良いでしょうか?

スタック トレースはワンライナーです。

 at company.product.Mvc.OracleSettingsStoreCache.VerifyValueInCacheOrInsert[T](T& returnVal, SettingsType settingType, String tenantId, String planId, String pageMnemonic, String processId, String transcationType, String language, String country, String wapTransactionType, String wapCodeGroup, String wapLoanReasons, String palleteType, Boolean isInsert, Object _cacheValue) in blahblahblah.OracleSettingsStoreCache.cs:line 290

コードのブロック全体は次のとおりです。

if (!string.IsNullOrEmpty(tenantId) && (!IsWacMode() || (IsWacMode() && settingType == OracleSettingsType.SettingsType.FetchWAPInvestmentTransfer)) && _useCache != "false")
                {
                    tenantId = tenantId.ToUpper().Trim();

                    _oracleTenantSettings = null;

                    if (_oracleCacheManager.Contains(_cacheKey))
                        _oracleTenantSettings = _oracleCacheManager.Get<OracleTenantSetting>(_cacheKey);

                    if (_oracleTenantSettings != null)
                    {
                        if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0)
                        {
                            _key = language + "_" + country + "_" + tenantId;
           ***LINE 290***   if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)
                            {
                                _objectMissing = TypeOfObjectMissing.TenantObjectDoesNotExist;
                            }
                        }
4

4 に答える 4

4

コードが存在するコンテキストを見ていないと、確信が持てません。しかし、あなたが説明する症状、つまり非常に散発的...説明できない...何かがヌルでありえないことに基づいて...私はスレッドの問題を強く疑うでしょう。たとえば、コレクションが静的であり、複数のスレッドによってアクセスされる可能性がある場合、最初のスレッドが何かが存在するかどうかをテストしてからアクセスするまでの間に、2番目のスレッドがコレクションの内容を変更する可能性があります(偶然のタイミングはまれですが)。その何か。

その場合は、コードをよりスレッドセーフにする必要があります。この問題を回避するには、ロックまたは同時収集を使用できます。ロックを使用するには、同期オブジェクトを使用する必要があります(その場で作成された新しいオブジェクトではありません)。また、そのコレクションにアクセスするすべての場所を探し出し、すべての場所をロックで囲む必要があります...コレクションを見るだけのコードは、コレクションを変更するコードだけでなく、ロックも使用する必要があります。これはSOの回答にとって大きなトピックなので、この非常に優れたリソースを使用することをお勧めします。

http://www.albahari.com/threading/

この場合、NREを取得する方法は次のとおりです。

thread 1 checks if entry exists in SortedList myList for _key="hello" 
gets true
thread 1 checks if entry for _key="hello" is non-null
gets true
thread 2 sets myList["hello"] = null
thread 1 executes myList["hello"].Something() and gets NRE.

あなたの投稿への編集に基づいて、これらの行にあるようです

if (_oracleTenantSettings != null) {
    if (_oracleTenantSettings.OraclePlanSettings != null && _oracleTenantSettings.OraclePlanSettings.Count > 0) {
        _key = language + "_" + country + "_" + tenantId;
         if (!_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) || _oracleTenantSettings.OraclePlanSettings[_key] == null)

NREが最後の行で発生した場合、最初の行または2番目の行を実行した直後に、別のスレッドをnullに設定_oracleTenantSettingsまたは_oracleTenantSettings.OraclePlanSettingsnullにすることができます。これらのいずれかが発生すると、最終行でNREがスローされます。

次のコードは、コードスレッドを安全にする適切な方法ではありませんが、この状況(null参照例外)の可能性が低くなるため、これが実際に当てはまるかどうかをすばやく確認する方法として役立つ場合があります。

var oracleTS = _oracleTenantSettings;
if (oracleTS != null) {
    var planSettings = oracleTS.OraclePlanSettings;
    if ((planSettings != null) && (planSettings.Count > 0)) {
        _key = language + "_" + country + "_" + tenantId;
        if (!planSettings.ContainsKey(_key) || planSettings[_key] == null)

最終行には、条件付きの最初の部分と2番目の部分の間で別のスレッドによってキーが削除されたり、テスト後にplanSettingsカウントが変更されたりするなど、スレッドに関連する他の問題がまだある可能性があることに注意してください。しかし、このコードがNREを大幅に削減する場合は、何が起こっているのかについてかなり良い手がかりがあり、必要に応じてロックを使用してコードスレッドを安全に処理する必要があります。さらに言えば、他のコード、特に_oracleTenantSettingsを変更するコードが何をしているのかをもっと知る必要があります。

于 2012-06-13T20:07:30.243 に答える
3

私の推測では、プロパティにアクセスする別のスレッドがあると思います。

これを修正する簡単な方法は、次のようにアクセスするたびにロックすることです。

var oraclePlanSettings = _oracleTenantSettings.OraclePlanSettings;
lock (oraclePlanSettings)
{
    // from now on you can safely access your cached reference "oraclePlanSettings"
    if (oraclePlanSettings != null && oraclePlanSettings.Count > 0)
        _oraclePlanSettings = oraclePlanSettings[_key]; // ... blabla
}

デッドロックに注意してください。

于 2012-06-13T20:14:45.260 に答える
1

以前の回答に同意します。おそらくスレッドの問題ですが、追加する必要があります。

これは、スレッド化されているかどうかを判断するための簡単で汚いテストです。

エラーを再現するシナリオを設定します (たとえば、一定のループで一晩中複数の (10) スレッドで実行する)。

この属性をクラスに適用します

【同期】

クラスは ContextBoundObject から継承する必要があります。

これにより、クラスのすべてのインスタンスが単一のスレッドで実行されます (かなり遅くなります)。

テストを再実行します。

問題が解決した場合は、スレッドの問題があります。速度が問題になる場合は、戻って、そのオブジェクトに関係するコードをすべてロックする必要があります。問題のオブジェクトのプロパティを使用するようにすべてを変換すると、ゲッターとセッターをロックできます。

手っ取り早いテストで問題が解決しない場合は、おそらく別の問題です。たとえば、安全でないコードまたは安全でない dll (つまり、非 .Net c++ で記述されたもの) を使用している場合、メモリ破損の問題である可能性があります。

お役に立てれば。

ContextBoundObject からの継承を含む、属性の詳細を次に示します。

さんのドキュメント

コードサンプル:

// Context-bound type with the Synchronization context attribute.
[Synchronization()]
public class SampleSynchronized : ContextBoundObject {

    // A method that does some work, and returns the square of the given number.
    public int Square(int i)  {

        Console.Write("The hash of the thread executing ");
        Console.WriteLine("SampleSynchronized.Square is: {0}", 
                             Thread.CurrentThread.GetHashCode());
        return i*i;
    }
}
于 2012-06-14T14:31:50.877 に答える
-1

アップデート

コードのどこかで、関連するオブジェクトの1つでEquality演算子または==がオーバーロードされており、nullが適切にチェックされていない(または失敗している)場合、または何かが等しい場合に等しいものとして返される場合に失敗が発生することを提案しています。いいえ。

この状況で使用されているクラスオブジェクトの==でのすべての演算子のオーバーロードを確認し、修正します。

オリジナル

ロジックをこれに変更します。最初にキーがないことを確認します...次に、有効なキーがあるが(および)その値がnullの場合に確認を行います。

 if ((_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key) == false) || 
     ((_oracleTenantSettings.OraclePlanSettings.ContainsKey(_key)) && 
       _oracleTenantSettings.OraclePlanSettings[_key] == null)))

元のステートメントのロジックフローを通して、断続的に失敗する理由を考えると、実際に予想されます。:-)

編集:説明させてください、ステップごとにこのロジックに従ってください

  1. 元のif句で(!ContainsKey(_key))を評価する場合、キーが存在しない(true)場合はFALSEに変更されることを意味します。
  2. 次に、#1のFalseのためにOrが開始します。OraclePlanSettings [_key]を評価しますが、キーが正しくありませんか?
  3. そのため、コードを実行してnullの無効なキーをチェックし、例外をスローします。

私が示したように論理を打ち破るだけで、例外は投げられません。

于 2012-06-13T20:38:39.187 に答える