동기화 기본 형식 개요
.NET Framework는 스레드의 상호 작용을 처리하고 경합 상태를 피하기 위한 다양한 동기화 기본 형식을 제공합니다. 이러한 기본 형식은 크게 잠금, 신호 및 연동 작업으로 분류할 수 있습니다.
범주가 명확히 정의되어 있지 않기 때문에 일부 동기화 메커니즘에 여러 범주의 특성이 있고, 한 번에 하나의 스레드를 해제하는 이벤트가 잠금과 비슷한 기능을 수행하고, 잠금 해제가 신호로 간주될 수 있고, 연동 작업이 잠금을 생성하는 데 사용될 수 있습니다. 그러나 범주는 여전히 유용합니다.
스레드 동기화는 공동 작업입니다. 즉, 스레드가 하나라도 동기화 메커니즘을 사용하지 않고 보호된 리소스에 직접 액세스하는 경우 이 동기화 메커니즘은 유효하지 않습니다.
이 개요는 다음과 같은 단원으로 구성됩니다.
잠금
신호
간단한 동기화 형식
SpinWait
연동 작업
잠금
잠금은 한 번에 하나의 스레드만 리소스를 제어할 수 있도록 하거나 지정한 수의 스레드가 리소스를 제어할 수 있도록 합니다. 잠금이 사용 중일 때 단독 잠금을 요청하는 스레드는 잠금을 사용할 수 있게 될 때까지 차단됩니다.
단독 잠금
가장 간단한 형식의 잠금은 코드 블록에 대한 액세스를 제어하는 C# lock 문(Visual Basic의 경우 SyncLock)입니다. 이러한 블록을 임계 영역이라고도 합니다. lock 문은 Monitor 클래스의 Enter 및 Exit 메서드를 사용하여 구현되며 try…catch…finally를 사용하여 잠금이 해제되도록 합니다.
일반적으로 lock 문을 사용하여 여러 메서드에 걸쳐 있지 않은 작은 코드 블록을 보호하는 것이 Monitor 클래스를 가장 효과적으로 사용하는 방법입니다. Monitor 클래스는 강력하기는 하지만 고아 잠금 상태와 교착 상태가 발생하기 쉽습니다.
Monitor 클래스
Monitor 클래스는 다음과 같이 lock 문과 함께 사용할 수 있는 추가 기능을 제공합니다.
TryEnter 메서드를 사용하면 지정한 간격 후에 차단된 스레드의 리소스 대기 상태를 해제할 수 있습니다. 이 메서드는 성공 또는 실패를 나타내는 부울 값을 반환합니다. 이 값은 잠재적인 교착 상태를 감지하여 막는 데 사용될 수 있습니다.
Wait 메서드는 임계 영역의 스레드에 의해 호출됩니다. 이 메서드는 리소스를 다시 사용할 수 있게 될 때까지 리소스와 블록을 제어하지 않습니다.
Pulse 및 PulseAll 메서드를 사용하면 잠금을 해제하거나 Wait를 호출할 스레드가 하나 이상의 스레드를 준비된 큐로 이동하여 잠금을 가져오게 할 수 있습니다.
Wait 메서드 오버로드에 대한 제한 시간은 대기 스레드가 준비된 큐로 이스케이프되게 할 수 있습니다.
잠금에 사용되는 개체가 MarshalByRefObject에서 파생되는 경우 Monitor 클래스는 여러 응용 프로그램 도메인에서 잠금을 제공할 수 있습니다.
Monitor에는 스레드 선호도가 있습니다. 즉, 모니터에 들어간 스레드는 Exit 또는 Wait를 호출하여 빠져 나와야 합니다.
Monitor 클래스는 인스턴스화할 수 없습니다. 해당 메서드는 정적(Visual Basic의 경우 Shared)이며 인스턴스화할 수 있는 잠금 개체에 대해 작동합니다.
개념적 개요를 보려면 Monitor를 참조하십시오.
Mutex 클래스
스레드는 해당 WaitOne 메서드의 오버로드를 호출하여 Mutex를 요청합니다. 시간 제한이 있는 오버로드는 스레드가 대기 상태를 해제할 수 있도록 하기 위해 제공됩니다. Monitor 클래스와 달리 뮤텍스는 지역적일 수도 있고 전역적일 수도 있습니다. 전역 뮤텍스는 명명된 뮤텍스라고도 하며 전체 운영 체제에서 표시되고 여러 응용 프로그램 도메인 또는 프로세스에서 스레드를 동기화하는 데 사용할 수 있습니다. 지역 뮤텍스는 MarshalByRefObject에서 파생되며 응용 프로그램 도메인 경계에 걸쳐 사용될 수 있습니다.
또한 Mutex는 WaitHandle에서 파생되므로 WaitAll, WaitAny 및 SignalAndWait 메서드와 같은 WaitHandle에서 제공하는 신호 메커니즘에 사용될 수 있습니다.
Monitor와 마찬가지로 Mutex에도 스레드 선호도가 있습니다. 그러나 Monitor와 달리 Mutex는 인스턴스화할 수 있는 개체입니다.
개념적 개요를 보려면 뮤텍스를 참조하십시오.
SpinLock 클래스
.NET Framework 버전 4부터는 Monitor에 필요한 오버헤드로 인해 성능이 저하될 때 SpinLock 클래스를 사용할 수 있습니다. SpinLock은 잠겨 있는 임계 영역을 만나면 잠금이 사용할 수 있게 될 때까지 단순히 루프에서 회전합니다. 잠금이 매우 짧은 시간 동안 보유되는 경우에는 회전이 블로킹보다 더 나은 성능을 제공할 수 있습니다. 그러나 잠금이 수십 사이클 이상 동안 보유되는 경우에는 SpinLock이 Monitor와 마찬가지로 작업을 수행하지만 CPU 사이클을 더 많이 사용하기 때문에 다른 스레드나 프로세스의 성능을 저하시킬 수 있습니다.
기타 잠금
잠금은 반드시 단독 잠금이 아니어도 됩니다. 단독 잠금은 일반적으로 제한된 수의 스레드가 리소스에 동시 액세스할 수 있도록 허용하는 데 유용합니다. 이러한 종류의 풀링 리소스 액세스를 제어하기 위해 세마포와 판독기 및 작성기 잠금이 설계되었습니다.
ReaderWriterLock 클래스
ReaderWriterLockSlim 클래스는 데이터를 변경하는 스레드인 작성기가 리소스에 독점적으로 액세스해야 하는 경우에 사용됩니다. 작성기가 활성화되어 있지 않으면 임의의 수의 판독기가 EnterReadLock 메서드를 호출하는 등의 방법으로 리소스에 액세스할 수 있습니다. 스레드에서 EnterWriteLock 메서드를 호출하는 등의 방법으로 단독 액세스를 요청하면 모든 기존 판독기가 잠금을 종료하고 작성기가 잠금을 시작 및 종료할 때까지 이후 판독기 요청이 차단됩니다.
ReaderWriterLockSlim에는 스레드 선호도가 있습니다.
개념적 개요를 보려면 판독기 및 작성기 잠금을 참조하십시오.
Semaphore 클래스
Semaphore 클래스를 사용하면 지정한 수의 스레드가 리소스에 액세스할 수 있습니다. 리소스를 요청하는 추가 스레드는 스레드가 세마포를 해제할 때까지 차단됩니다.
Mutex 클래스와 마찬가지로 Semaphore는 WaitHandle에서 파생됩니다. 또한 Mutex와 마찬가지로 Semaphore는 지역적일 수도 있고 전역적일 수도 있습니다. 이 클래스는 응용 프로그램 도메인 경계에 걸쳐 사용될 수 있습니다.
Monitor, Mutex 및 ReaderWriterLock과 달리 Semaphore에는 스레드 선호도가 없습니다. 즉, 이 클래스는 하나의 스레드가 세마포를 가져오고 다른 스레드가 이 세마포를 해제하는 시나리오에서 사용할 수 있습니다.
개념적 개요를 보려면 세마포 및 SemaphoreSlim를 참조하십시오.
System.Threading.SemaphoreSlim은 단일 프로세스 경계 안에 있는 간단한 동기화 세마포입니다.
맨 위로 이동
신호
다른 스레드의 신호를 기다리는 가장 간단한 방법은 다른 스레드가 완료될 때까지 차단하는 Join 메서드를 호출하는 것입니다. Join에는 지정된 시간 간격이 지나면 차단된 스레드가 대기 상태를 벗어날 수 있는 두 개의 오버로드가 있습니다.
대기 핸들은 보다 다양한 대기 및 신호 기능을 제공합니다.
대기 핸들
대기 핸들은 WaitHandle 클래스에서 파생되고 이 클래스는 MarshalByRefObject에서 파생됩니다. 따라서 대기 핸들은 응용 프로그램 도메인 경계에서 스레드 활동을 동기화하는 데 사용할 수 있습니다.
스레드는 WaitOne 인스턴스 메서드나 WaitAll, WaitAny 또는 SignalAndWait 정적 메서드 중 하나를 호출하여 대기 핸들을 차단합니다. 해제 방법은 호출된 메서드와 해당 대기 핸들의 유형에 따라 다릅니다.
개념적 개요를 보려면 대기 핸들을 참조하십시오.
이벤트 대기 핸들
이벤트 대기 핸들에는 EventWaitHandle 클래스와 해당 파생 클래스인 AutoResetEvent 및 ManualResetEvent가 포함됩니다. 스레드는 해당 Set 메서드를 호출하거나 SignalAndWait 메서드를 사용하여 이벤트 대기 핸들에 신호를 보낼 때 이벤트 대기 핸들에서 해제됩니다.
이벤트 대기 핸들은 신호를 보낼 때마다 하나의 스레드만 허용되는 회전문과 같이 자동으로 다시 설정되거나, 신호를 보내면 열리고 다른 사용자가 닫으면 다음 신호까지 그 상태가 유지되는 게이트와 같이 수동으로 다시 설정해야 합니다. 이름에서 알 수 있듯이 AutoResetEvent와 ManualResetEvent는 각각 전자와 후자를 나타냅니다. System.Threading.ManualResetEventSlim은 단일 프로세스 경계 안에 있는 간단한 동기화 이벤트입니다.
EventWaitHandle은 이벤트 형식을 나타내므로 지역 또는 전역입니다. 파생 클래스인 AutoResetEvent와 ManualResetEvent의 형식은 항상 지역입니다.
이벤트 대기 핸들에는 스레드 선호도가 없습니다. 모든 스레드는 이벤트 대기 핸들에 신호를 보낼 수 있습니다.
개념적 개요를 보려면 EventWaitHandle, AutoResetEvent, CountdownEvent 및 ManualResetEvent를 참조하십시오.
Mutex 및 Semaphore 클래스
Mutex 및 Semaphore 클래스는 WaitHandle에서 파생되므로 WaitHandle의 정적 메서드에서 사용할 수 있습니다. 예를 들어, 스레드는 WaitAll 메서드를 사용하여 세 가지 해당 조건이 충족될 때까지 대기할 수 있습니다. 즉, EventWaitHandle이 신호를 받아야 하고 Mutex가 해제되어야 하며 Semaphore도 해제되어야 합니다. 마찬가지로 스레드는 WaitAny 메서드를 사용하여 이 세 가지 조건 중 하나가 충족될 때까지 대기할 수 있습니다.
Mutex 또는 Semaphore의 경우 신호를 받는다는 것은 해제됨을 의미합니다. 둘 중 하나의 형식을 SignalAndWait 메서드의 첫 번째 인수로 사용하면 메서드가 해제됩니다. 스레드 선호도가 있는 Mutex의 경우 호출 스레드에 뮤텍스가 없으면 예외가 throw됩니다. 앞에서 설명한 대로 세마포에는 스레드 선호도가 없습니다.
장벽
Barrier 클래스를 사용하면 여러 스레드를 주기적으로 동기화하여 이러한 스레드가 모두 동일한 지점에서 차단되고 다른 모든 스레드가 완료될 때까지 기다리도록 할 수 있습니다. 장벽은 하나 이상의 스레드가 다음 알고리즘 단계로 이동하기 전에 다른 스레드의 결과를 필요로 할 때 유용합니다. 자세한 내용은 장벽(.NET Framework)을 참조하십시오.
맨 위로 이동
간단한 동기화 형식
.NET Framework 4부터는 가능한 경우 항상 부담이 큰 대기 핸들과 같은 Win32 커널 개체를 사용하지 않음으로써 빠른 성능을 제공하는 동기화 기본 형식을 사용할 수 있습니다. 일반적으로 대기 시간이 짧고 원래 동기화 형식이 적합하지 않은 경우에만 이러한 형식을 사용해야 합니다. 프로세스 간 통신이 필요한 시나리오에서는 간단한 형식을 사용할 수 없습니다.
System.Threading.SemaphoreSlim은 System.Threading.Semaphore의 단순화된 버전입니다.
System.Threading.ManualResetEventSlim은 System.Threading.ManualResetEvent의 단순화된 버전입니다.
System.Threading.CountdownEvent는 카운트가 0이 되면 신호를 받는 이벤트를 나타냅니다.
System.Threading.Barrier를 사용하면 마스터 스레드의 제어 없이도 여러 스레드를 서로 동기화할 수 있습니다. 장벽은 모든 스레드가 지정된 지점에 도달할 때까지 개별 스레드가 다음 단계로 이동하지 못하도록 합니다.
맨 위로 이동
SpinWait
.NET Framework 4부터는 메서드가 신호를 받거나 조건이 충족될 때까지 스레드가 대기해야 할 때 System.Threading.SpinWait 구조를 사용할 수 있습니다. 그러나 대기 핸들을 사용하거나 현재 스레드를 블로킹하여 실제 대기 시간이 필요한 대기 시간보다 짧을 것으로 예상되는 경우에는 이 구조를 사용할 수 없습니다. SpinWait를 사용하면 대기하는 동안 회전할 시간을 짧게 지정하고 지정된 시간에 조건이 충족되지 않은 경우에만 대기하도록 지정할 수 있습니다.
맨 위로 이동
연동 작업
연동 작업은 Interlocked 클래스의 정적 메서드에 의해 메모리 위치에서 수행되는 간단한 원자 작업입니다. 이러한 원자 작업에는 비교 내용에 따라 추가, 증가 및 감소, 교환, 조건부 교환과, 32비트 플랫폼의 64비트 값에 대한 읽기 작업이 포함됩니다.
참고 |
---|
원자성에 대한 보장은 개발 작업으로 제한됩니다. 즉, 여러 작업을 하나의 단위로 수행해야 하는 경우 덜 정교한 동기화 메커니즘을 사용해야 합니다. |
이 작업은 잠금 또는 신호는 아니지만 잠금과 신호를 생성하는 데 사용할 수 있습니다. 또한 연동 작업은 Windows 운영 체제의 기본 작업이므로 속도가 매우 빠릅니다.
연동 작업은 강력한 비차단 동시성을 수행하는 응용 프로그램을 작성하기 위한 휘발성 메모리에 사용할 수 있지만 복잡한 하위 수준의 프로그래밍이 필요합니다. 대부분의 경우 간단한 잠금을 사용하는 것이 좋습니다.
개념적 개요를 보려면 연동 작업를 참조하십시오.
맨 위로 이동
참고 항목
개념
기타 리소스
EventWaitHandle, AutoResetEvent, CountdownEvent 및 ManualResetEvent