스레드 경합 및 동기화로 인한 문제
[OS] 스레드 동기화 문제 - Race Condition, Deadlock, Starvation, Livelock
스레드 동기화 문제 멀티프로세싱이나 멀티스레딩을 개발할 때 프로세스나 스레드들의 상태 제어 처리하는 부분에 까다로운 점이 많습니다. 스레드의 개수가 많아질수록 더욱 정교한 처리가
cheetile.tistory.com
경합 상황(여러 스레드가 동일한 메모리에 동시 접근 후 write할 경우. read는 문제X) 발생 시 가능한 해결법들:
1) Lock(mutex)를 이용한 상호배타적 접근 (Mutual exclusion)
2) Atomic한 연산을 이용
가시성과 원자성으로 인한 문제
이 중 캐치하기 쉽지 않은 가시성 문제에 특히 유의.
atomic<int64> num = 0;
void Thread_1()
{
num.store(1);
}
void Thread_2()
{
num.store(2);
}
void Thread_3()
{
num.store(3);
}
void Thread_Observer()
{
while (true)
{
int64 value = num.load();
// 관찰 결과 출력
}
}
store와 load가 atomic한 연산임에도 위 코드대로 스레드1,2,3을 만들고,
1 실행 후 observe, 2 실행 후 observe, 3 실행 후 observer 하더라도
옵저버의 관찰 결과는 0, 1, 2, 3 이렇게 규칙적으로 나오지 않는다.
0, 1, 1, 3 또는 0, 0, 2, 2 이런 식의 결과가 나올 수 있다.
그 이유를 알아보기 전에, 한가지 중요한 점을 짚고 넘어가자.
과거에 나온 결과가 미래보다 앞설 수는 없다.
무슨 말이냐하면, 현재 우리는 num의 값이 0 - 1 - 2 - 3으로 변화함을 atomic 연산을 통해 보장받는 중이다. 고로 실제로도 값은 0, 1, 2, 3을 순서대로 저장하게 될 것이다.
그러나 이를 읽어올 때, 설사 방금 스레드 1이 실행되어 num의 메모리에 1이 저장했음에도, Thread_Observer에서 num을 load할 때는 여전히 0이 나올 수도 있다.
이유는 메모리 계층화와 관련이 있다. (아래 글 참고)
https://velog.io/@syleemk/Java-Concurrent-Programming-가시성과-원자성
[Java] Concurrent Programming - 가시성과 원자성
멀티 스레드 프로그래밍 소개 멀티 스레드를 다루는 과정의 기초가 되는 가시성과 원자성을 정의해볼 것 사실 가시성과 원자성이라고 하는 단어는 문제를 해결하기 위한 원칙이다. 바꿔 말해 Mu
velog.io
RAM과 캐시 사이의 값 동기화 시점이 언제인지 명확하지 않기 때문에, 특정 시점에서 num의 값을 스레드에서 서로 다르게 읽을 수도 있다는 것에 유의하자.
컴파일러, CPU 최적화로 인한 문제

CPU 파이프라인은 일을 하나씩 밀어내기식으로 작업하는데 이때 특정 작업을 먼저 하는게 연산결과에 상관이 없지만 속도는 더 빠를 경우 이를 컴파일러/CPU에서 캐치해 변경해버리는 경우가 있음. (16 - CPU 파이프라인 참고)
'os > multithreading, parallel computing' 카테고리의 다른 글
메모리 정책(memory order), 메모리 장벽(memory fence/barrier) (0) | 2022.09.10 |
---|---|
lock-free atomic calculation (0) | 2022.09.10 |
스레드의 race condition (0) | 2021.05.26 |
쓰레드와 프로세스, CPU 병렬 처리와 동시 처리 (0) | 2021.01.03 |