int32 sum = 0; // 공유 데이터
void Add()
{
for (int32 i = 0; i < 100'0000; i++)
{
sum++;
}
}
void Sub()
{
for (int32 i = 0; i < 100'0000; i++)
{
sum--;
}
}
int main()
{
Add();
Sub();
cout << sum << endl;
}
메인스레드에서 실행하면 공유데이터인 sum의 값이 0으로, 정상적으로 출력된다.
int main()
{
std::thread t1(Add);
std::thread t2(Sub);
t1.join();
t2.join();
cout << sum << endl; // 멀티스레드 환경에서는 오답
}
그러나 t1, t2에서 병렬적으로, 즉 멀티쓰레드 환경에서 Add, Sub을 각각 실행하면 엉뚱한 숫자가 나오는 것을 볼 수 있다.
첫번째 줄이 메인쓰레드, 두번째 줄이 멀티쓰레드
C++로 sum++
이라는 간단한 코드는 어셈블리어로는 3줄짜리 코드이다.
int eax = sum;
eax = eax + 1;
sum = eax;
즉 CPU에서 실제로 sum++
이 실행될 때는 이러한 3줄의 코드가 실행되기 때문에, Add
와 Sub
가 3줄의 코드를 한 번에 순차적으로 실행한다는 보장이 없기 때문에 공유데이터의 결과가 0이 아닌 것이다.
Atomic, 번역하면 원자.
즉 Add
나 Sub
의 코드는 모두 실행되거나, 모두 실행되지 않아야 한다.(All-Or-Nothing)
#include <atomic>
atomic<int32> sum = 0; // 공유 데이터
void Add()
{
for (int32 i = 0; i < 1000000; i++)
{
sum.fetch_add(1);
}
}
void Sub()
{
for (int32 i = 0; i < 1000000; i++)
{
sum.fetch_sub(1);
}
}
공유데이터 sum
이 atomic 타입이면 멀티쓰레드 환경에서 만약 Add
의 sum++
가 먼저 실행됐을 경우 sum++
가 모두 끝난 후 Sub
의 sum--
가 실행된다.
따라서 정상적으로 0이라는 답이 도출된다.
코드
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <atomic>
atomic<int32> sum = 0; // 공유 데이터
void Add()
{
for (int32 i = 0; i < 1000000; i++)
{
sum.fetch_add(1);
}
}
void Sub()
{
for (int32 i = 0; i < 1000000; i++)
{
sum.fetch_sub(1);
}
}
int main()
{
Add();
Sub();
cout << sum << endl;
std::thread t1(Add);
std::thread t2(Sub);
t1.join();
t2.join();
cout << sum << endl; // 멀티스레드 환경에서는 오답
}