반응형
원자적 작업(Atomic Operation)란?
**원자적 작업(Atomic Operation)**은 더 이상 나눌 수 없는 단일 작업 단위를 의미합니다. 즉, 어떤 작업이 원자적이라고 할 때, 이는 해당 작업이 중단되거나 다른 작업과 간섭받지 않고 한 번에 완전히 실행된다는 것을 뜻합니다.
원자적 작업의 특성
- 불가분성 (Indivisibility):
- 작업이 시작되면 완료될 때까지 외부의 개입 없이 수행됩니다.
- 작업이 중간에 중단될 가능성이 없으며, 다른 스레드가 해당 작업에 간섭할 수 없습니다.
- 중단 방지 (Interrupt-Proof):
- 다른 스레드가 해당 변수나 자원에 접근하려 하더라도 작업이 완료된 이후에만 접근이 가능합니다.
- 일관성 (Consistency):
- 원자적 작업은 일관된 상태를 보장합니다. 작업이 성공하면 그 결과가 완전히 반영되고, 실패하면 아무런 변화도 없습니다.
원자적 작업의 예
비원자적 작업의 문제
아래는 원자적이지 않은 작업의 예입니다.
int counter = 0;
void Increment()
{
counter++;
}
이 작업은 사실 세 가지 단계로 나뉘어집니다:
- counter의 현재 값을 읽음.
- 읽은 값에 1을 더함.
- 결과를 counter에 씀.
이 세 단계 중 다른 스레드가 동일한 변수(counter)를 읽거나 수정하면 경합 조건이 발생할 수 있습니다.
원자적 작업의 구현
위 작업을 Interlocked를 사용하여 원자적으로 처리하면, 하나의 단일 작업으로 처리됩니다.
void Increment()
{
Interlocked.Increment(ref counter);
}
Interlocked.Increment(ref counter)는 내부적으로 하드웨어 명령어를 사용하여 값을 한 번에 증가시킵니다. 이 작업은 다음을 보장합니다:
- 값의 읽기, 수정, 쓰기 과정이 단일 작업으로 묶임.
- 작업이 중단되거나 간섭받지 않음.
원자적 작업의 예시와 활용
- 증가와 감소:
- 간단한 카운터에서 주로 사용됩니다.
int counter = 0; // 원자적으로 증가 Interlocked.Increment(ref counter); // 원자적으로 감소 Interlocked.Decrement(ref counter);
- 값 교환:
- 특정 변수의 값을 새로운 값으로 변경하면서, 이전 값을 반환합니다.
int value = 42; // value를 100으로 변경하고 이전 값을 oldValue에 저장 int oldValue = Interlocked.Exchange(ref value, 100); Console.WriteLine($"Old Value: {oldValue}, New Value: {value}");
- 조건부 값 변경:
- 변수의 값이 특정 값(comparand)일 때만 새로운 값으로 설정합니다.
int currentValue = 10; int newValue = 20; // currentValue가 10이면 20으로 변경 int originalValue = Interlocked.CompareExchange(ref currentValue, newValue, 10); Console.WriteLine($"Original Value: {originalValue}, Current Value: {currentValue}");
- 값 더하기:
- 특정 값을 기존 값에 더합니다.
int total = 0; // total에 5를 원자적으로 더함 Interlocked.Add(ref total, 5); Console.WriteLine($"Total: {total}");
원자적 작업의 실제 사용 사례
- 스레드 안전한 카운터:
- 다중 스레드 환경에서 동기화를 사용하지 않고 값을 안전하게 증감합니다.
int counter = 0; void IncrementCounter() { Interlocked.Increment(ref counter); }
- 플래그 설정 및 교환:
- 스레드 간의 플래그를 설정하거나 교환할 때 사용됩니다.
int isProcessing = 0; void StartProcessing() { if (Interlocked.Exchange(ref isProcessing, 1) == 0) { Console.WriteLine("Processing started."); } }
- 락 없이 공유 상태 관리:
- 락을 사용하지 않고 간단한 공유 상태를 관리할 때 사용합니다.
int sharedValue = 0; void UpdateSharedValue() { Interlocked.Add(ref sharedValue, 10); }
원자적 작업이 중요한 이유
- 다중 스레드 환경에서 작업이 중단 없이 실행되므로 경합 조건을 방지할 수 있습니다.
- lock 또는 Monitor와 비교하여 성능이 더 우수하며, 간단한 값 조작에는 적합합니다.
- 프로세스나 스레드 간의 일관된 상태를 보장합니다.
한계점:
- 단일 변수에 대한 작업만 원자적으로 보장되며, 여러 변수나 복잡한 작업에는 적합하지 않습니다. 이 경우에는 lock 또는 Monitor 같은 동기화 메커니즘을 사용해야 합니다.
원자적 작업은 경량 동기화를 제공하는 강력한 도구로, 다중 스레드 프로그래밍에서 효율적이고 안전한 방법으로 사용됩니다.
반응형
'C# > 쓰레드' 카테고리의 다른 글
C# `Thread`와 `ThreadPool`의 차이점 및 델리게이트 비동기 호출 이해 (16) | 2025.01.16 |
---|---|
C# 스레드풀(Thread Pool): 개념, 사용법, 및 예제 코드 설명 (1) | 2025.01.16 |
C# SemaphoreSlim: 멀티스레드 리소스 접근 제어 예제와 설명 (1) | 2025.01.16 |
C# 스레드 제어: Semaphore와 SemaphoreSlim의 차이와 사용법 (2) | 2025.01.16 |
C# Mutex로 프로세스 단일 인스턴스 제어하기 (1) | 2025.01.16 |
스레드 우선순위와 CPU 시간 할당 이해하기 (2) | 2025.01.14 |
스레드_스레드 상태 조사_thread state (4) | 2025.01.06 |
c# 스레드 기초 (1) | 2025.01.06 |