ListDictionary는 System.Collections.Specialized 네임스페이스에 포함된 키-값(key/value) 컬렉션으로, 내부적으로 연결 리스트 방식으로 데이터를 저장합니다. 이 컬렉션은 기본적으로 스레드로부터 안전(thread-safe)하지 않으므로, 멀티스레드 환경에서 안전하게 읽거나 쓰려면 SyncRoot 속성과 C#의 lock 문을 조합해 동기화된 액세스를 구현해야 합니다.
예제 1: SyncRoot와 lock으로 동기화된 열거
다음 예제에서는 ListDictionary에 항목을 추가한 뒤, 주요 속성(IsFixedSize, IsReadOnly, IsSynchronized)을 확인하고 lock(dict.SyncRoot) 블록 안에서 컬렉션 전체를 순회합니다.
using System;
using System.Collections;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
ListDictionary dict = new ListDictionary();
dict.Add("1", "SUV");
dict.Add("2", "Sedan");
dict.Add("3", "Utility Vehicle");
dict.Add("4", "Compact Car");
dict.Add("5", "SUV");
dict.Add("6", "Sedan");
dict.Add("7", "Utility Vehicle");
dict.Add("8", "Compact Car");
dict.Add("9", "Crossover");
dict.Add("10", "Electric Car");
Console.WriteLine("ListDictionary 요소...");
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
Console.WriteLine("\nListDictionary의 크기가 고정되어 있습니까? = " + dict.IsFixedSize);
Console.WriteLine("ListDictionary가 읽기 전용입니까? = " + dict.IsReadOnly);
Console.WriteLine("ListDictionary가 동기화되어 있습니까? = " + dict.IsSynchronized);
Console.WriteLine("ListDictionary에 'K' 키가 있습니까? = " + dict.Contains("K"));
Console.WriteLine("ListDictionary에 '9' 키가 있습니까? = " + dict.Contains("9"));
Console.WriteLine("\n동기화된 액세스...");
lock(dict.SyncRoot) {
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
}
}
}
실행 결과
위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
ListDictionary 요소... 1 SUV 2 Sedan 3 Utility Vehicle 4 Compact Car 5 SUV 6 Sedan 7 Utility Vehicle 8 Compact Car 9 Crossover 10 Electric Car ListDictionary의 크기가 고정되어 있습니까? = False ListDictionary가 읽기 전용입니까? = False ListDictionary가 동기화되어 있습니까? = False ListDictionary에 'K' 키가 있습니까? = False ListDictionary에 '9' 키가 있습니까? = True 동기화된 액세스... 1 SUV 2 Sedan 3 Utility Vehicle 4 Compact Car 5 SUV 6 Sedan 7 Utility Vehicle 8 Compact Car 9 Crossover 10 Electric Car
핵심 포인트 정리
- IsSynchronized: ListDictionary 자체의 동기화 여부를 나타내며, 기본값은
False입니다. 즉, 별도의 동기화 처리가 필요합니다. - SyncRoot: 컬렉션에 대한 잠금(lock)에 사용할 수 있는 객체를 반환합니다. 이 객체를 대상으로
lock문을 작성하면 여러 스레드가 동시에 컬렉션을 수정하거나 열거하는 상황을 방지할 수 있습니다. - Contains(): 특정 키가 컬렉션에 존재하는지 확인합니다. 위 예제에서 'K'는 존재하지 않고 '9'는 존재함을 확인했습니다.
예제 2: 간단한 동기화 액세스 예시
이번에는 문자열 키를 사용해 더 간결한 형태로 동기화된 열거를 수행해 보겠습니다.
using System;
using System.Collections;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
ListDictionary dict = new ListDictionary();
dict.Add("A", "Books");
dict.Add("B", "Electronics");
dict.Add("C", "Appliances");
dict.Add("D", "Pet Supplies");
dict.Add("E", "Clothing");
dict.Add("F", "Footwear");
Console.WriteLine("동기화된 액세스...");
lock(dict.SyncRoot) {
foreach(DictionaryEntry d in dict) {
Console.WriteLine(d.Key + " " + d.Value);
}
}
}
}
실행 결과
동기화된 액세스... A Books B Electronics C Appliances D Pet Supplies E Clothing F Footwear
마무리
ListDictionary는 소규모 컬렉션에서 빠른 성능을 제공하지만, 기본적으로 동기화되지 않으므로 멀티스레드 환경에서는 반드시 lock(dict.SyncRoot) 패턴을 사용해야 합니다. 참고로 스레드 안전성이 중요한 최신 .NET 개발에서는 System.Collections.Concurrent 네임스페이스의 ConcurrentDictionary<TKey, TValue>를 대안으로 고려하는 것도 좋은 방법입니다.