QNX RTOS: 4-11. Atomic Operations 실험
2025. 11. 27. 17:35ㆍ운영체제/QNX
요약
- Case 1 (mutex)
- 한 번에 한 스레드만 들어와서
- var1, var2 관계 + 값 자체 둘 다 깔끔하게 유지 → 0, 0
- race 없음, 로그도 안 찍힘
- Case 2 (no mutex, non-atomic)
- ++/--가 쪼개져서 여러 스레드가 섞이며 일부 증가/감소가 유실
- var1, var2가 종종 어긋나서 로그 찍힘
- 중간중간 var1 = var2;로 관계는 맞추지만
전체 합은 0이 아니게 남아서 마지막이 (274, 274)
- Case 3 (no mutex, atomic)
- 각 변수의 ++/--는 atomic이라 증가/감소 유실은 없음
- 하지만 “if ~ printf ~ ++/-- 전체 시퀀스”는 atomic이 아니라서
여전히 var1 != var2 순간이 생기고 로그가 찍힘 - 그래도 합은 정확히 0으로 돌아가서 마지막이 (0, 0)
Case 1, Mutex 사용
void *
update_thread(void *i)
{
while (!done)
{
pthread_mutex_lock(&var_mutex);
if (var1 != var2)
{
printf("thread %d, var1 (%u) is not equal to var2 (%u)!\n", pthread_self(), var1, var2);
var1 = var2;
}
var1++;
var1--;
var2++;
var2--;
pthread_mutex_unlock(&var_mutex);
}
return (NULL);
}
결과
all done, var1 is 0, var2 is 0
- 한 번에 오직 한 스레드만 이 블록 안에 들어올 수 있음.
- 따라서 어떤 스레드가 들어오든, 루프 한 번에 실행되는 일은:
- if (var1 != var2)
- 처음엔 둘 다 0이니 항상 false.
- var1++; var1--; → 결과적으로 var1 변함 없음
- var2++; var2--; → var2도 변함 없음
- if (var1 != var2)
- 이게 수백만 번 반복돼도, 항상 (var1, var2) = (0, 0) 유지.
Case 2, mutex 없음 + 일반 ++/-- (race condition)
void *
update_thread(void *i)
{
while (!done)
{
if (var1 != var2)
{
printf("thread %d, var1 (%u) is not equal to var2 (%u)!\n", pthread_self(), var1, var2);
var1 = var2;
}
var1++;
var1--;
var2++;
var2--;
}
return (NULL);
}
결과
thread 2, var1 (274) is not equal to var2 (274)!
thread 3, var1 (274) is not equal to var2 (274)!
thread 5, var1 (275) is not equal to var2 (274)!
thread 2, var1 (275) is not equal to var2 (274)!
thread 3, var1 (275) is not equal to var2 (274)!
all done, var1 is 274, var2 is 274
여기서 두 가지 포인트:
- if (var1 != var2)가 참이 된 시점에서는 진짜로 값이 달랐는데,
그 사이에 다른 스레드가 var1 = var2;를 실행해서
printf에 찍힐 땐 둘이 같아질 수도 있어요.
→ 그래서 로그 상으로는 같은 값인데 “not equal” 메세지가 찍히는 모습이 보이는 것. - 마지막에 (274, 274)로 끝난 이유는
- 중간중간 if (var1 != var2)가 걸릴 때마다
var1 = var2;로 “맞춰주는 동작”이 들어가고, - 동시에 레이스 때문에 ++/--가 완전히 상쇄되지 못해
어딘가에서 누적된 값이 274만큼 남아버린 것.
- 중간중간 if (var1 != var2)가 걸릴 때마다
즉,
- 관계(var1 == var2)는 어찌어찌 유지하려고 하지만
- 절대값(0으로 유지)은 레이스로 망가진 상태라고 보면 됨.
Case 3, mutex 없음 + atomic_add/atomic_sub 사용
void *
update_thread(void *i)
{
while (!done)
{
if (var1 != var2)
{
printf("thread %d, var1 (%u) is not equal to var2 (%u)!\n", pthread_self(), var1, var2);
var1 = var2;
}
atomic_add(&var1, 1);
atomic_sub(&var1, 1);
atomic_add(&var2, 1);
atomic_sub(&var2, 1);
}
return (NULL);
}
결과
thread 5, var1 (0) is not equal to var2 (0)!
thread 2, var1 (1) is not equal to var2 (0)!
thread 4, var1 (1) is not equal to var2 (0)!
thread 5, var1 (0) is not equal to var2 (0)!
thread 2, var1 (1) is not equal to var2 (0)!
thread 3, var1 (1) is not equal to var2 (0)!
thread 4, var1 (0) is not equal to var2 (0)!
all done, var1 is 0, var2 is 0
- Case 2: 일반 var1++ / var1-- 때문에 “증가/감소가 유실”
→ 전체적으로 0에서 벗어나서 274만큼 쌓여버린 상태에서 종료 - Case 3: atomic_add & atomic_sub 덕분에
각 변수에 대한 증가/감소는 절대 유실되지 않음
즉, Case 3에서는:
- atomic_add(&var1, 1)
- atomic_sub(&var1, 1)
이 두 개는 스레드가 몇 개가 되든, 어떤 순서로 섞이든
각 호출이 정확히 한 번씩 실행되는 이상 합이 0이 보장됨.
var2도 마찬가지.
그래서,
- 중간에는 레이스 때문에 if (var1 != var2)가 종종 참이 되고,
- 그때마다 var1 = var2;로 관계를 맞춰주고,
- ++/--는 정확히 상쇄되기 때문에
전체적으로 드리프트 없이 0으로 회귀하는 형태가 되는 것.
→ 그래서 마지막이 all done, var1 is 0, var2 is 0으로 끝남!!
정리
- “atomic 연산 = 멀티 변수/멀티 단계 전체를 지켜주는 마법의 락이 아니다.”
- “여러 변수의 관계(invariant)를 보호하고 싶으면 여전히 mutex 같은 상위 레벨 동기화가 필요하다.”
- “그래도 atomic을 쓰면 최소한 이 변수 하나에 대해서는 증가/감소 유실 같은 건 막을 수 있다.”
'운영체제 > QNX' 카테고리의 다른 글
| QNX RTOS: 5-1. Message Passing (0) | 2025.11.27 |
|---|---|
| QNX RTOS: 5. Interprocess Communication (0) | 2025.11.27 |
| QNX RTOS: 4-11. Synchronization - Atomic Operations (0) | 2025.11.27 |
| QNX RTOS: 4-10. Conditional Variables 실험 (0) | 2025.11.27 |
| QNX RTOS: 4-10. Synchronization - Conditional Variables (0) | 2025.11.27 |