2012-11-25 3 views
1

System.Collections.Generic.SynchronizedCollection 공유 컬렉션이 있습니다. 우리 코드는 .Net 4.0 Task 라이브러리를 사용하여 스레드를 확장하고 동기화 된 컬렉션을 스레드에 전달합니다. 지금까지 스레드는 컬렉션에 항목을 추가하거나 제거하지 않았습니다. 그러나 스레드 중 하나를 필요로하는 새로운 요구 사항은 컬렉션에서 항목을 제거해야하지만 다른 스레드는 컬렉션을 읽어야합니다. 컬렉션에서 항목을 제거하기 전에 잠금을 추가해야합니까? 그렇다면 독자 스레드는 스레드로부터 안전할까요? 또는 스레드 안전을 얻는 가장 좋은 방법을 제안 하시겠습니까?동기화 된 컬렉션 스레드 안전

+0

SynchronizedCollection을 사용하는 경우 컬렉션에서 추가/제거하는 작업에 대해 적절한 잠금을 제공해야합니다. – Joe

+0

다른 스레드에서 항목을 제거하는 동안 다른 스레드에서 컬렉션을 열거하는 것을 지원합니까? 열거 섹션을 잠글 필요가 있습니까? – Amzath

+0

"SynchronizedCollection"의 모든 포인트는 모든 작업 (모든 작업에서'잠금 ')으로 스레드 안전 콜렉션을 제공하는 것입니다. ICollection 에서 무언가를 사용한다면 잠재적으로 스스로 잠글 필요가 있습니다. 4.0 프레임 워크 이상을 대상으로하는 경우 System.Collections.Concurrent를 사용하면 성능이 향상 될 수 있습니다. – Joe

답변

2

예, SynchronizedCollection에서 잠금을 수행합니다.

독자가 많고 작가가 한 명이라면 SynchronizedCollection 대신 ReaderWriterLock을 사용하는 것이 좋습니다.

또한 .Net 4+ 인 경우 System.Collections.Concurrent을 살펴보십시오. 이러한 클래스는 SynchronizedCollection보다 훨씬 뛰어난 성능을 제공합니다.

+1

동기화 된 컬렉션은 여러 독자와 한 명의 작성자가 안전하게 사용할 수 있습니까? – Amzath

+0

예, 그렇지만 효율적이 아닙니다. –

+0

컬렉션이 사용되는 경우에 따라 다르다고 생각합니다. 예 : 특정 값으로 컬렉션에서 항목을 찾으려고 할 때 (예 :속성 값이 20보다 큰 항목)을 선택하고 해당 항목을 변경하면 원자 조작이 없지만 잠금이 필요합니다. – Offler

3

아니요 완전히 스레드로부터 안전하지 않습니다. 당신이 ConcurrentBag로 SynchronizedCollection를 교체 할 경우, 당신은 스레드 안전을 얻을 것이다,

var collection = new SynchronizedCollection<int>(); 

var n = 0; 

Task.Run(
    () => 
     { 
      while (true) 
      { 
       collection.Add(n++); 
       Thread.Sleep(5); 
      } 
     }); 

Task.Run(
    () => 
     { 
      while (true) 
      { 
       Console.WriteLine("Elements in collection: " + collection.Count); 

       var x = 0; 
       if (collection.Count % 100 == 0) 
       { 
        foreach (var i in collection) 
        { 
         Console.WriteLine("They are: " + i); 
         x++; 
         if (x == 100) 
         { 
          break; 
         } 

        } 
       } 
      } 
     }); 

Console.ReadKey(); 

enter image description here

참고 : 다음 간단한 콘솔 응용 프로그램에서 다음을 시도하고 예외와 충돌하는 방법을 참조

var collection = new ConcurrentBag<int>(); 

SynchronizedCollection은이 응용 프로그램에서 스레드로부터 안전하지 않습니다. 대신 동시 컬렉션을 사용하십시오.

4

알렉산더는 이미 SynchronizedCollection이이 시나리오에서는 안전하지 않다는 것을 지적했습니다. SynchronizedCollection은 실제로 일반 제네릭 목록을 래핑하고 호출을 둘러싼 잠금을 사용하여 모든 호출을 기본 목록에 위임합니다. 이는 GetEnumerator에서도 이루어집니다. 따라서 열거자를 가져 오는 것은 동기화되지만 실제 열거는 가져 오지 않습니다.

var collection = new SynchronizedCollection<string>(); 
collection.Add("Test1"); 
collection.Add("Test2"); 
collection.Add("Test3"); 
collection.Add("Test4"); 

var enumerator = collection.GetEnumerator(); 
enumerator.MoveNext(); 
collection.Add("Test5"); 
//The next call will throw a InvalidOperationException ("Collection was modified") 
enumerator.MoveNext(); 

foreach를 사용할 때 열거자를 이러한 방식으로 호출합니다. 따라서이 배열을 통해 열거하기 전에 ToArray()을 추가하면이 배열에 처음 열거되므로이 배열을 통해 열거되지 않습니다. 이 열거 형은 foreach 내부에서 수행중인 작업이 더 빨라질 수 있으므로 동시성 문제가 발생할 가능성을 줄일 수 있습니다. Richard가 지적한 바에 따르면 진정한 스레드 안전성을 위해서는 System.Collections.Concurrent 클래스를 사용하십시오.

+0

그러나 ToArray를 호출하기 전에 컬렉션의 SyncRoot 속성을 사용할 수 있습니다. –