C#의 StringCollection은 기본적으로 스레드로부터 안전(thread-safe)하지 않은 컬렉션입니다. 따라서 여러 스레드가 동시에 동일한 컬렉션에 접근하는 환경에서는 SyncRoot 속성과 lock 문을 사용하여 동기화된 액세스를 구현해야 합니다. SyncRoot는 컬렉션에 대한 동기화를 수행하는 데 사용되는 전용 객체를 반환하며, 이를 lock 블록으로 감싸면 한 번에 하나의 스레드만 컬렉션에 접근할 수 있습니다.
예제 1: 배열 요소 추가 후 동기화 액세스로 순회하기
using System;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
StringCollection stringCol = new StringCollection();
String[] arr = new String[] { "100", "200", "300", "400", "500" };
Console.WriteLine("Array elements...");
foreach (string res in arr) {
Console.WriteLine(res);
}
stringCol.AddRange(arr);
Console.WriteLine("Total number of elements = "+stringCol.Count);
stringCol.RemoveAt(3);
Console.WriteLine("Total number of elements now = "+stringCol.Count);
Console.WriteLine("Synchronize access...");
lock(stringCol.SyncRoot) {
foreach(object ob in stringCol) {
Console.WriteLine(ob);
}
}
}
}실행 결과
위 코드를 실행하면 다음과 같은 출력이 생성됩니다.
Array elements... 100 200 300 400 500 Total number of elements = 5 Total number of elements now = 4 Synchronize access... 100 200 300 500
위 예제에서는 먼저 문자열 배열을 만들어 AddRange() 메서드로 StringCollection에 한 번에 추가했습니다. 이후 RemoveAt(3)을 호출하여 인덱스 3(값 400)의 요소를 삭제했고, 마지막으로 lock(stringCol.SyncRoot) 블록 안에서 foreach 루프를 통해 모든 요소를 안전하게 출력했습니다.
예제 2: Add 메서드로 요소 추가 후 동기화 액세스
이번에는 Add() 메서드로 개별 문자열을 하나씩 추가하고, 같은 방식으로 동기화된 액세스를 적용해 보겠습니다.
using System;
using System.Collections.Specialized;
public class Demo {
public static void Main() {
StringCollection strCol = new StringCollection();
strCol.Add("Katie");
strCol.Add("Philips");
strCol.Add("Jacob");
strCol.Add("Carl");
strCol.Add("Gary");
Console.WriteLine("Synchronize access...");
lock(strCol.SyncRoot) {
foreach(object ob in strCol) {
Console.WriteLine(ob);
}
}
}
}실행 결과
Synchronize access... Katie Philips Jacob Carl Gary
핵심 정리
- SyncRoot: 컬렉션 내부의 동기화 객체를 반환하며, 외부 코드에서 lock 대상으로 사용됩니다.
- lock 문: 임계 영역(critical section)을 지정하여 한 번에 하나의 스레드만 해당 블록을 실행하도록 보장합니다.
- 열거 중 안전성: foreach로 컬렉션을 순회하는 도중 다른 스레드가 요소를 수정하면 오류가 발생할 수 있으므로, 순회 시 반드시 SyncRoot로 잠그는 것이 좋습니다.
이처럼 StringCollection을 멀티스레드 환경에서 사용할 때는 SyncRoot 속성과 lock 문을 함께 활용하면 데이터 경합(race condition)과 예기치 않은 예외를 효과적으로 방지할 수 있습니다.