개발/C, C++
C++ - atomic으로 원자적 연산 처리하기 (vs mutex)
피로물든딸기
2024. 1. 26. 20:41
반응형
원자성 (Atomicity)
- 연산이 도중에 중단되지 않고 전체가 완료되거나 전혀 수행되지 않는 것
- 원자성이 지켜져야 멀티스레드 환경에서 데이터의 일관성을 유지하고 동시성 문제를 해결할 수 있다.
다음 코드를 실행해 보자.
#include <iostream>
#include <thread>
using namespace std;
int counter;
void incrementCounter()
{
for (int i = 0; i < 1000000; i++) counter++;
}
int main()
{
thread t1(incrementCounter);
thread t2(incrementCounter);
t1.join();
t2.join();
cout << "Counter : " << counter << endl;
return 0;
}
두 개의 스레드가 counter 변수를 동시에 증가시키고 있다.
따라서 원하는 값인 2000000가 아니라 1000000 ~ 2000000 사이의 값이 나오게 된다.
Atomic (원자 연산)
C++의 std::atomic은 원자 연산을 통해 데이터의 일관성을 보장한다.
특정 연산이 다른 스레드에 의해 중단되지 않으며 전체적으로 완료되도록 한다.
다음과 같이 counter 변수를 atomic으로 선언하면 된다.
#include <atomic>
std::atomic<int> counter(0);
아래와 같이 수정하면 정상적으로 Counter : 2000000을 얻는다.
#include <iostream>
#include <atomic>
#include <thread>
using namespace std;
atomic<int> counter(0);
void incrementCounter()
{
for (int i = 0; i < 1000000; i++) counter++;
}
int main()
{
thread t1(incrementCounter);
thread t2(incrementCounter);
t1.join();
t2.join();
cout << "Counter : " << counter << endl;
return 0;
}
Mutex (상호배제)
C++의 std::mutex는 여러 스레드가 동시에 공유 데이터에 접근하는 것을 막는다.
Critical Section에 진입할 때 해당 스레드는 뮤텍스를 잠그거나 해제해서 데이터를 보호한다.
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
mutex counterMutex;
int counter = 0;
void incrementCounter()
{
for (int i = 0; i < 1000000; i++)
{
lock_guard<mutex> lock(counterMutex);
counter++;
}
}
int main()
{
thread t1(incrementCounter);
thread t2(incrementCounter);
t1.join();
t2.join();
cout << "Counter : " << counter << endl;
return 0;
}
반응형