본문 바로가기
개발/C, C++

C++ - atomic으로 원자적 연산 처리하기 (vs mutex)

by 피로물든딸기 2024. 1. 26.
반응형

C, C++ 전체 링크

 

원자성 (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;
}

반응형

댓글