2009-02-09 4 views
24

시간과 시간 다시 BindingList 및 ObservableCollection의 스레드 안전 버전을 작성해야하는 이유는 UI에 바인딩 할 때 이러한 컨트롤을 여러 스레드에서 변경할 수 없기 때문입니다. 제가 이해하려고하는 것은 입니다. 이유는 이것이인데, 이것은 설계상의 잘못입니까, 아니면 의도적 인 행동입니까?BindingList 또는 ObservableCollection과 같은 클래스가 스레드로부터 안전하지 않은 이유는 무엇입니까?

답변

31

스레드 안전 컬렉션 디자인 문제는 간단하지 않습니다. 물론 상태를 손상시키지 않고 여러 스레드에서 수정하거나 읽을 수있는 컬렉션을 디자인하는 것은 간단합니다. 그러나 여러 스레드에서 업데이트되면 주어진 컬렉션을 디자인하는 것이 훨씬 어렵습니다. 다음 코드를 예로 들어 보겠습니다.

if (myCollection.Count > 0) { 
    var x = myCollection[0]; 
} 

myCollection은 추가 및 업데이트로 상태가 손상되지 않도록 보장되는 스레드 안전 수집이라고 가정합니다. 이 코드는 스레드로부터 안전하지 않으며 경쟁 조건입니다.

왜? myCollection이 안전하더라도 myCollection : namedly와 indexer의 두 메서드 호출간에 변경이 발생하지 않는다고 보장 할 수 없습니다. 다른 스레드가 들어 와서이 호출 사이의 모든 요소를 ​​제거 할 수 있습니다.

이 유형의 문제는 매우 솔직히 악몽입니다. 한 호출의 반환 값이 콜렉션의 후속 호출에 영향을 줄 수는 없습니다. http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

+0

나는 똑같은 문제가 있습니다. Dispatcher 나 다른 방법을 사용하여 백그라운드 스레드에서 BindingList에 항목을 추가하려면 어떻게합니까? – Houman

+0

예, 간단히 말해서 .NET 콜렉션 * 인터페이스 자체의 디자인은 스레드 안전성에 적합하지 않습니다. Jared가 지적한 것처럼 Count 속성은 멀티 스레드 환경에서는 쓸모가 없습니다. –

+0

스레드 안전 형식으로 구현 될 수있는 'IList '기능의 많은 유용한 하위 집합이 있습니다. 물건을 제거하거나 주변을 움직이는 항목은 문제가 될 수 있지만 나머지 인터페이스는 많은 응용 프로그램에서 유용한 하위 집합이됩니다. 추가 된 항목의 색인을보고 한 '추가'버전이 도움이 될 수 있지만 모든 응용 프로그램이 필요하지는 않습니다. 추가 된 모든 항목이 목록 수명 동안 동일한 슬롯에 계속 존재한다면 스레드 안전 'IList '구현은 외부 잠금이없는 많은 멀티 스레드 시나리오에서 유용 할 수 있습니다. – supercat

6

야렛의 훌륭한 대답에 약간을 추가하려면 : 스레드 안전을 무료로 제공되지 않습니다

편집

나는 최근 블로그 게시물에서이 토론을 확장했다. 많은 (대부분?) 컬렉션은 단일 스레드에서만 사용됩니다. 멀티 스레드 케이스를 처리하기 위해 성능 또는 기능상의 불이익을받는 이유는 무엇입니까?

+0

글쎄, 어쩌면 나는 그것을 다음과 같이 바꿔야한다. 왜 프레임 워크가 ThreadSafeObservableCollection 또는 그와 같은 것을 제공하지 않는가? –

+0

그건 좀 더 합리적인 질문입니다.하지만 Jared의 대답이 시작됩니다. 이것은 정말로 "thread-safe"가 의미하는 것에 달려 있습니다. 이것은 간단한 yes/no 플래그가 아닙니다. –

2

미친 듯이 가고 싶다면 - here'sThreadedBindingList<T>이 자동으로 UI 스레드에 다시 알림을 보냅니다. 그러나 한 번에 한 스레드 만 업데이트를 수행하는 것이 안전 할 수 있습니다. 에서 귀하의 질문에

변경

: 모든 다른 답변에서 아이디어를 수집

+0

이 구현은 동기화 컨텍스트 스레드에 '추가'만 마샬링합니다. 목록을 열거 할 때 여러 경쟁 조건 및/또는 컬렉션 수정 오류를 방지하지 못합니다. – piers7

5

, 나는 이것이 당신의 문제를 해결하는 가장 간단한 방법이라고 생각 "왜 제정신 클래스 X 아니다"

에 "클래스 X와 함께이 일의 온건 한 방법은 무엇입니까?" 당신이 당신의 관찰 컬렉션을 만들 때 클래스의 생성자에서

  1. 는 현재 displatcher를 얻을. 당신이 지적한대로 원래 스레드에서 수정이 필요합니다. 메인 GUI 스레드가 아닐 수도 있습니다. 그래서 App.Current.Dispatcher은 올바르게 작동하지 않으며 이며 모든 클래스가 입니다 .Dispatcher.

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; 
    _data = new ObservableCollection<MyDataItemClass>(); 
    
  2. 는 코드 섹션 원래 스레드를 필요 를 호출하는 발송자를 사용합니다. 당신을 위해 트릭을 할해야

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); })); 
    

. 상황이 있지만 을 선호 할 수도 있습니다. 대신 .BeginInvoke .Invoke을 입력하십시오.

관련 문제