2012-01-03 5 views
3

이 사항이 동기화 문제가 발생할 것인지 궁금 :스레드 안전하게 접근 <T> 속성

List<Character> characters = World.CharacterManager.Characters; 

'문자'는 다음과 같이 보일 것이다 클래스

'CharacterManager.Characters'입니다 :

public List<Character> Characters 
{ 
    get 
    { 
     lock (this.objLock) { return this.characters; } 
    } 
} 

동기화 문제가 발생합니까?

찾고있는 캐릭터를 찾으려면 반복되는 참조 된 목록을 사용하고 싶습니다.

+1

더 많은 컨텍스트가 필요합니다. 잠글 필요가 있습니까? 여러 스레드를 사용하고 있습니까? 그 자물쇠는 실제로 아무 것도 얻지 못합니다. – Gary

+3

단일 스레드 컨텍스트에서 동기화에 대해 묻지 않으므로 멀티 스레딩이 안전하게 수행 될 수 있다고 생각합니다. – Phil

+0

감사합니다. Phil. 예,이 상황이 멀티 스레드 응용 프로그램에 대한 것인지 확인할 수 있습니다. – TheAJ

답변

2

잠금 그렇게 쓸모가 없다. 당신은 윌 제안과 같은 스레드 안전 수집을 사용해야 할 것, 또는 쓰기 액세스를 필요로하지 않는 경우에 당신과 같이 목록의 읽기 전용 버전을 노출 할 수 있습니다 :

public ReadOnlyCollection<Character> Characters { 
    get { 
    lock (locker) { return this.characters.AsReadOnly(); } 
    } 
} 

이 컬렉션은 수정할 수 없습니다 따라서 Character 유형이 불변 인 경우 동기화 문제가 발생하지 않습니다. Character이 변경 가능하면 다시 문제가 발생하지만 스레드 안전 수집에서도 문제가 발생합니다. 네가 그 사실을 알고 있기를 바란다. IList<Character>을 반환하는 속성을 노출 할 수도 있지만 대개 호출자에게 객체가 읽기 전용임을 알리는 것이 좋습니다.

쓰기 액세스가 필요한 경우 CharacterManager의 범위에서 적절한 방법을 제공하고 동기화 할 수도 있습니다. Jesse는 이것을 수행하는 방법에 대한 좋은 예를 작성했습니다.

편집 : ICollection에 SyncRoot가 없습니다. <T>.

+0

List 은 명시 적으로'SyncRoot' 만 구현하므로, 액세스하려면'ICollection'으로 형 변환 (cast)해야합니다. (참조 : http://stackoverflow.com/a/4067371/3312) –

+0

감사합니다. 또한 검색 한 결과 [이 패턴을 중단했습니다] (오래 전부터) (http://blogs.msdn.com/b/bclteam/archive/2005/03/15/396399.aspx). 나는 그 대답을 편집했다. – Andreas

+0

고마워요. 내가 뭔가를 호출하면 : World.CharacterManager.Characters.Count 그것은 여전히 ​​쓸모없는 자물쇠가 될까요? – TheAJ

9

get 동안 잠금을 설정했지만 각 스레드가 컬렉션에 대한 참조를 갖게되면 문제가 동시에 해결 될 수 있습니다. List<T>의 멤버는 스레드로부터 안전하지 않으므로 컬렉션을 반복, 추가, 제거 할 때 임의의 버그와 예외가 발생합니다.

아마 스레드로부터 안전한 컬렉션을 반환해야합니다. 100 % 호환 가능한 스레드 안전 버전이 없으므로 System.Collections.Concurrent을보고 사용할 수있는 버전을 찾아야합니다.

0

호출자가 목록을 열거 할 수있게하려는 경우 속성의 형식은 IEnumerable이어야합니다. 이 경우 해당 목록의 사본을 만들어 사본을 반환합니다. 목록을 열거하는 동안 목록이 변경되면 유효하지 않게되고 예외가 발생합니다. 트레이드 오프는 발신자가 최신 버전의 목록을 가지고 있지 않을 수도 있다는 것입니다. 호출자가 각 호출에 대해 업데이트 된 목록을 가져와야한다는 것을 나타내는 데 도움이되는 속성 대신 GetCharactersAsOfNow()이라는 메서드로 변환하려고합니다.

그러나 호출자가 목록을 수정할 수 있도록 허용하려는 경우 목록이 스레드로부터 안전하지 않으며 호출자가 스레드 동기화를 수행해야합니다. 호출자가 이제이 책임을진다는 것을 감안하면 더 이상 속성 지터에 자물쇠가 필요하지 않습니다.

1

실제로 호출 코드가 목록에 추가 및 제거 할 수 있어야합니까? 그렇다면 not considered a best practice입니다. 여기에 대신 추가하고 CharacterManager 클래스 자체에 Character 항목의 제거두고, 그 요구 사항없이 구현하는 (가능한) 방법 :

internal sealed class CharacterManager 
{ 
    private readonly IList<Character> characters = new List<Character>(); 

    public ReadOnlyCollection<Character> Characters 
    { 
     get 
     { 
      lock (this.characters) 
      { 
       return this.characters.AsReadOnly(); 
      } 
     } 
    } 

    public void Add(Character character) 
    { 
     lock (this.characters) 
     { 
      this.characters.Add(character); 
     } 
    } 

    public void Remove(Character character) 
    { 
     lock (this.characters) 
     { 
      this.characters.Remove(character); 
     } 
    } 
} 
+0

몇 초만에 나를 때려 눕힌다 :-) – Andreas

+0

기본'Character' 클래스의 변경 가능성 문제를 언급 할 때 +1을받습니다. –

관련 문제