CPU는 명령어를 아래와 같은 여러 단계로 나누어 병렬로 실행한다.
📌 모든 명령어가 위 단계들을 거치며, 각 명령어는 서로 다른 시점에 병렬로 처리된다. 이 구조 덕분에 성능이 향상되지만, 동시에 순서 보장이 어려워지는 문제가 있다.
int32 x = 0, y = 0, r1 = 0, r2 = 0;
volatile bool ready = false;
void Thread_1() {
while (!ready);
y = 1;
r1 = x;
}
void Thread_2() {
while (!ready);
x = 1;
r2 = y;
}
int main() {
int count = 0;
while (true) {
ready = false;
count++;
x = y = r1 = r2 = 0;
thread t1(Thread_1);
thread t2(Thread_2);
ready = true;
t1.join();
t2.join();
if (r1 == 0 && r2 == 0)
break;
}
cout << count << " 번만에 빠져나옴.\n";
}
r1 == 0 && r2 == 0는 절대 일어나지 않음.코드 재배치(Code Reordering)
store, load 간 연관이 없다고 판단하면 순서를 변경하여 성능을 높이려 한다.가시성(Visibility) 문제
std::atomic)#include <atomic>
std::atomic<int> x(0), y(0);
int r1, r2;
void Thread_1() {
x.store(1, std::memory_order_relaxed);
r1 = y.load(std::memory_order_relaxed);
}
void Thread_2() {
y.store(1, std::memory_order_relaxed);
r2 = x.load(std::memory_order_relaxed);
}
std::atomic은 동기화와 가시성을 보장한다.memory_order_relaxed는 순서를 보장하진 않지만 데이터 경쟁 없이 안전한 접근 가능.std::mutex)#include <mutex>
std::mutex mtx;
void Thread_1() {
std::lock_guard<std::mutex> lock(mtx);
x = 1;
r1 = y;
}
std::mutex는 동시에 하나의 스레드만 공유 자원에 접근하도록 보장이렇게 파이프라인 방식으로 처리하면 CPU는 더 많은 일을 동시에 수행할 수 있다. 하지만 순서 문제가 생길 수도 있음에 유의해야 한다!