2017-03-20 1 views
1

예외 (나를 위해 ..) 예외를 만났습니다. 단지 드물게 발생하지만,하지 ...Null 참조 기본 생성자 이후 예외

내 클래스는 정적이 아니라 하나의 정적 속성이 있습니다 :

내가 처음으로 추가하기 위해 노력하고있어
static Dictionary<string, ManualResetEvent> resetEvents = 
    new Dictionary<string, ManualResetEvent>(); 

리셋 이벤트 - 때로 Null 참조 예외가 발생합니다. 인스턴스를 추가하려고하는 두 개의 다른 스레드와 관련이 있습니까?

static ManualResetEvent resetEventsGet(string key) 
{ 
    if (resetEvents.ContainsKey(key)) 
     return resetEvents[key]; 
    ManualResetEvent reste = new ManualResetEvent(false); 
    resetEvents.Add(key, reste); //System.NullReferenceException: 'Object reference not set to an instance of an object.' HOW??? 
    return reste; 
} 

"시계"또는 즉치 창에서 아무 곳이나 (사전 또는 resetEvent) null이 표시되지 않습니다.

p.s - 코드가 변경되지 않았지만 이전에 결코 발생하지 않았기 때문에 visual studio 2017에 태그를 지정했습니다. 아이디어가 있으십니까? 고마워요

+0

'ContainsKey' 호출을 지나쳐서 보이기 때문에 이상합니다. 어디서나 당신은'resetEvents = null'을 가지고 있지 않습니까? – juharr

+0

그것은 나에게 이해가 가지 않는다. 예외가'if (resetEvents.ContainsKey (key))'줄에 표시되지 않는 이유는 무엇입니까? 코드의 다른 부분에서 해당 필드가 'null'이되지 않도록 하시겠습니까? – dcg

+0

'new ManualResetEvent (false) '에 대한 호출은 무엇을합니까? 필드가 'null'이되지는 않습니까? – Marcello

답변

4

resetEventsGet을 여러 스레드에서 호출하면 완벽하게 가능합니다. Dictionary.Add은 스레드로부터 안전하지 않으며 여러 스레드에서 호출 할 때 'NullReferenceException'이 발생하는 등 이상한 일이 발생할 수 있습니다. 항상

class Program { 
    static Dictionary<string, ManualResetEvent> resetEvents = new Dictionary<string, ManualResetEvent>(); 

    static void Main() 
    { 
     for (int i = 0; i < 1000; i++) { 
      new Thread(() => 
      { 
       resetEvents.Add(Guid.NewGuid().ToString(), new ManualResetEvent(false)); 
      }) 
      { 
       IsBackground = true 
      }.Start(); 
     } 
     Console.ReadKey(); 
    }  
} 

이 코드는하지만, 매우 자주, Dictionary.Insert 개인 방법 내부 null 참조 예외가 발생에는 다음과 같은 코드로 재현하는 것은 상대적으로 쉽다.

사전은 값을 배열과 같은 내부 구조에 저장하고 이러한 구조는 고정 크기가 아니기 때문에 발생합니다. 더 많은 값을 추가 할 때 사전은 내부 구조의 크기를 조정할 수 있으며 다른 스레드가 동시에 그 값을 열거하면 크기가 조정될 수 있습니다. 크기 조정과 열거를 동시에 수행하면 null 참조 또는 범위를 벗어나는 예외를 비롯한 많은 나쁜 점이 발생할 수 있습니다.

그렇다면 적절한 잠금을 사용하지 마십시오. 또는 ConcurrentDictionary<string, ManualResetEvent>과 같은 다중 스레드 액세스 용으로 설계된 모음을 사용하십시오.

+0

고마워요! 마침내 테스트 관리 - 사실 나는 visual studio 2008 (framWork 3.5)에 대한 예외가 없었습니다. 2017 년에는 입력하지 않은 keyValues ​​(1000을 채우려고 시도했지만 998 만 찾았습니다 ... 예외없이)를 제외하고는 Guid 사전 루프 어레이를 시작한 후 거의 없었습니다. Intresting. 다시 한번 감사드립니다. 분명히 lock \ ConcurrentDictionary를 지금 추가합니다. – ephraim

+0

하 !! 마침내 VS 2008에서도 나에게 일어났습니다 ... 실제 차이점은 VS 2008 릴리스 모드에서는 절대 실행되지 않았지만 2017은 내가 한 일이라고 생각합니다. 이제 릴리스 모드에서 2008 년에 문제가 발생합니다. – ephraim

3

이 스레드에 여러 스레드로 액세스하는 경우이를 잠그는 것이 좋습니다. 문제는 사전이 스레드 안전하지 않다는 것입니다. 이 경우 사전 자체를 lock 객체로 사용할 수 있습니다. (비공개이기 때문에)

뭔가 같은 :

static ManualResetEvent resetEventsGet(string key) 
{ 
    lock(resetEvents) 
    { 
     ManualResetEvent result; 

     // lookup the key 
     if(!resetEvents.TryGetValue(key, out result)) 
      // if it doesn't exists, create a new one. 
      resetEvents.Add(key, result = new ManualResetEvent(false)); 

     return result; 
    } 
} 

는 또한 TryGetValue이 크다. 그것은 당신에게 가치와 그것이 존재하는지를 알려줍니다. (두 개가 아닌 하나의 조회 만)