2009-09-15 4 views
2

System.Collections.Generic.List<T>을 지울 때 내가 왜 IndexOutOfRangeException인지 알 수 없습니다. 이게 말이 돼?리스트 지우기 문제 <T>

List<MyObject> listOfMyObject = new List<MyObject>(); 
listOfMyObject.Clear(); 
+2

인가 :

이것은 간단한 옵션이 될 것인가? 예외의 스택 추적은 어떻게 생겼습니까? –

+0

왜 목록을 작성한 직후에 명확하게 전화하겠습니까?별로 의미가 없습니다. 이것은 실제 코드가 아닙니다 ... –

+4

문제를 설명하는 짧지 만 완전한 프로그램을 게시하십시오. –

답변

18

이것은 일반적으로 여러 스레드가 목록에 동시에 액세스하는 경우 발생합니다. 다른 스레드가 Clear()를 호출하는 동안 한 스레드가 요소를 삭제하면이 예외가 발생할 수 있습니다.

이 경우의 "대답"은 적절하게 동기화하여 모든 목록 액세스를 잠그는 것입니다.


편집 :

이 문제를 처리하기 위해, 가장 간단한 방법은 사용자 정의 클래스 내에서 목록을 캡슐화하고, 당신이 필요로하는 방법을 노출하지만, 필요에 따라 잠그는 것입니다. 컬렉션을 변경하는 모든 것에 잠금을 추가해야합니다. 어떤 제어 목록 바인딩이

public class MyClassCollection 
{ 
    // Private object for locking 
    private readonly object syncObject = new object(); 

    private readonly List<MyObject> list = new List<MyObject>(); 
    public this[int index] 
    { 
     get { return list[index]; } 
     set 
     { 
      lock(syncObject) { 
       list[index] = value; 
      } 
     } 
    } 

    public void Add(MyObject value) 
    { 
     lock(syncObject) { 
      list.Add(value); 
     } 
    } 

    public void Clear() 
    { 
     lock(syncObject) { 
      list.Clear(); 
     } 
    } 
    // Do any other methods you need, such as remove, etc. 
    // Also, you can make this class implement IList<MyObject> 
    // or IEnumerable<MyObject>, but make sure to lock each 
    // of the methods appropriately, in particular, any method 
    // that can change the collection needs locking 
} 
+0

예 동시 스레드가 있으므로 어떻게 잠글 수 있습니까? 아마도 싱글 톤으로 구현하는 것이 좋겠지 만 스레드 안전을 보장하지는 않습니까? – paradisonoir

+0

아니요, 트릭은 목록을 변경해야 할 때마다 언제든지 목록을 잠그고 싶다는 것입니다. 내 대답을 편집하여 기본 구현을 보여줄 것입니다. –

7

코드에서 예외가 발생 했습니까? 나는 가지고있다

using System.Collections.Generic; 

class MyObject { } 

class Program { 
    static void Main(string[] args) { 
     List<MyObject> listOfMyObject = new List<MyObject>(); 
     listOfMyObject.Clear(); 
    } 
} 

나는 예외를 얻지 않는다.

실제 사례가 더 복잡합니까? 아마도 여러 스레드가 동시에 목록에 액세스하고 있습니까? 스택 추적을 볼 수 있습니까?

List<T>.Clear은 매우 간단합니다. 리플 렉터 사용 :

public void Clear() { 
    if (this._size > 0) { 
     Array.Clear(this._items, 0, this._size); 
     this._size = 0; 
    } 
    this._version++; 
} 

목록이 이미 비어있는 경우 예외가 발생하지 않습니다. 그러나 다른 스레드에서 목록을 수정하는 경우 Array.ClearIndexOutOfRangeException 예외를 throw 할 수 있습니다. 따라서 다른 스레드가 목록에서 항목을 제거하면 this._size (지울 항목 수)이 너무 커집니다.

1

설명서에이 메서드가 throw되는 예외는 언급되어 있지 않지만 문제는 아마도 다른 곳에서 발생합니다.
List<T>.Clear

+2

그러나 Array.Clear는'IndexOutOfRangeException'을 throw 할 수 있습니다. 'List .Clear'는'Array.Clear'에 의존하기 때문에'List .Clear'는 멀티 쓰레드 시나리오에서'Array.Clear'에서'IndexOutOfRangeException'을 트리거 할 수 있습니다. – jason

+0

글쎄, 잘 알고있다. 문서에서는 표준 인스턴스 멤버가 스레드 안전성이 보장되지 않는다고 말합니다. 홀수 멀티 밟기가 질문에 언급되지 않았습니다 (또는 wpf가 충분히 가깝습니까?) – Kobi

+0

Kobi, 맞습니다. 멀티 스레딩을 사용하고 있습니다. – paradisonoir

관련 문제