Computer >> 컴퓨터 >  >> 프로그래밍 >> C#

C#에서 스택(Stack)에 대한 동기화된 액세스를 구현하는 방법

멀티스레드 환경에서는 여러 스레드가 동시에 하나의 Stack 컬렉션에 접근할 수 있으며, 이 경우 데이터 손상이나 예기치 않은 오류가 발생할 수 있습니다. 이러한 문제를 방지하려면 lock 문과 SyncRoot 속성을 함께 사용하여 동기화된 액세스를 구현해야 합니다.

스택 동기화의 기본 원리

System.Collections 네임스페이스의 Stack 클래스는 기본적으로 스레드로부터 안전(thread-safe)하지 않습니다. 특히 foreachStack 클래스가 제공하는 SyncRoot 속성은 컬렉션에 대한 액세스를 동기화하는 데 사용되는 객체를 반환하며, 이 객체를 lock 블록으로 감싸면 해당 영역에서 한 번에 하나의 스레드만 컬렉션에 접근하도록 보장할 수 있습니다.

예제 1: 정수 요소를 가진 스택 동기화

using System;
using System.Collections;
public class Demo {
    public static void Main() {
        Stack stack = new Stack();
        stack.Push(100);
        stack.Push(200);
        stack.Push(300);
        stack.Push(400);
        stack.Push(500);
        Console.WriteLine("Stack...");
        foreach(Object ob in stack) {
            Console.WriteLine(ob);
        }
        Console.WriteLine("Count of elements = "+stack.Count);
        Console.WriteLine("Synchronize access...");
        lock(stack.SyncRoot) {
            foreach(Object ob in stack) {
                Console.WriteLine(ob);
            }
        }
    }
}

출력 결과

위 코드를 실행하면 다음과 같은 결과가 출력됩니다.

Stack...
500
400
300
200
100
Count of elements = 5
Synchronize access...
500
400
300
200
100

스택은 LIFO(Last-In-First-Out, 후입선출) 구조이므로 마지막에 Push한 500부터 순서대로 출력됩니다. lock(stack.SyncRoot) 블록 내부의 열거 작업은 다른 스레드의 간섭 없이 안전하게 수행됩니다.

예제 2: 문자열 요소를 가진 스택 동기화

이번에는 문자열 데이터를 저장한 스택에 동일한 방식으로 동기화된 액세스를 적용해 보겠습니다.

using System;
using System.Collections;
public class Demo {
    public static void Main() {
        Stack stack = new Stack();
        stack.Push("Jacob");
        stack.Push("Tim");
        stack.Push("Philips");
        stack.Push("Tom");
        stack.Push("Amy");
        stack.Push("Katie");
        stack.Push("Selena");
        stack.Push("Taylor");
        stack.Push("Justin");
        Console.WriteLine("Stack...");
        foreach(Object ob in stack) {
            Console.WriteLine(ob);
        }
        Console.WriteLine("\nSynchronize access...");
        lock(stack.SyncRoot) {
            foreach(Object ob in stack) {
                Console.WriteLine(ob);
            }
        }
    }
}

출력 결과

Stack...
Justin
Taylor
Selena
Katie
Amy
Tom
Philips
Tim
Jacob

Synchronize access...
Justin
Taylor
Selena
Katie
Amy
Tom
Philips
Tim
Jacob

핵심 정리 및 추가 팁

  • SyncRoot 속성은 컬렉션 자체를 잠그는 것이 아니라, 동기화에 사용되는 전용 객체를 반환합니다.
  • lock 블록의 범위는 실제로 공유 데이터에 접근하는 최소한의 영역으로 유지하는 것이 성능 면에서 좋습니다.
  • .NET 2.0 이후 환경에서는 타입 안전성이 보장되는 제네릭 컬렉션(Stack<T>)을 사용하고, 멀티스레드 시나리오에서는 System.Collections.Concurrent 네임스페이스의 ConcurrentStack<T>를 활용하는 것이 더 권장됩니다.

이처럼 lock 문과 SyncRoot 속성을 조합하면 레거시 Stack 컬렉션에서도 스레드 간 충돌 없이 안전하게 데이터를 처리할 수 있습니다.