Atomic

나무·2023년 5월 17일
1
post-thumbnail

Atomic이란?

Atomic이란 "분할 불가능한"이라는 의미로, 여러 스레드에서 동시에 접근하는 공유 데이터의 일관성을 유지하기 위한 도구다. Atomic 연산은 All-Or-Nothing 원칙을 따르는데, 이는 연산이 완전히 수행되거나, 아니면 전혀 수행되지 않아야 함을 의미한다. 이를 통해 멀티스레드 환경에서 공유 데이터를 안전하게 처리할 수 있다.

Atomic의 특징

Atomic 객체는 원자적 연산을 제공한다. 이는 여러 스레드에서 동시에 접근해도 데이터의 일관성을 보장한다는 뜻이다. 하지만, 이러한 보장을 위해 필요한 동기화 과정 때문에 일반적인 연산에 비해 비용이 더 들 수 있다. 따라서 성능이 중요한 상황에서는 Atomic 연산의 사용에 주의해야 한다. 또한, Atomic 연산이 제공하는 보장은 연산 수준에서만 유효하다. 따라서 여러 연산이 함께 동작해야 하는 복잡한 동기화 작업에는 락과 같은 더 고수준의 동기화 도구가 여전히 필요하다.

예제 코드

#include "pch.h"
#include <iostream>
#include <thread>
#include <atomic>

atomic<int32> sum = 0;

void Add()
{
	for (int32 i = 0; i < 100'0000; i++)
	{
		sum.fetch_add(1);
	}
}

void Sub()
{
	for (int32 i = 0; i < 100'0000; i++)
	{
		sum.fetch_add(-1);
	}
}

int main()
{
	std::thread t1(Add);
	std::thread t2(Sub);

	t1.join();
	t2.join();

	cout << sum << '\n';

	return 0;
}

이 코드에서는 두 개의 스레드(t1, t2)를 생성하고, 각각 Add()와 Sub() 함수를 실행한다. Add() 함수에서는 sum 값을 1,000,000번 증가시키고, Sub() 함수에서는 sum 값을 1,000,000번 감소시킨다. 이때 sum 값의 변경은 fetch_add() 함수를 통해 원자적으로 수행된다.

정리

Atomic은 멀티스레드 환경에서 공유 데이터를 안전하게 관리하기 위한 중요한 도구다. 원자성을 보장하는 연산을 제공하므로 데이터 일관성을 유지할 수 있다. 하지만 성능에 영향을 미칠 수 있으므로, 필요에 따라 사용해야 하며, 더 복잡한 동기화 작업에는 락 등의 도구를 함께 사용하는 것이 좋다. 이번에는 atomic를 사용하여 sum 변수를 선언하고 fetch_add() 함수를 사용하여 값을 안전하게 증가시키거나 감소시켰다. 이를 통해 멀티스레드 환경에서 경쟁 조건(race condition)을 피할 수 있었다.

하지만, 이렇게 Atomic 객체를 사용하면 속도 관련 이슈가 발생할 수 있었다. 이는 Atomic 객체가 원자적 연산을 보장하기 위해 스레드 간 동기화를 수행하기 때문이다. 따라서 성능이 중요한 상황에서는 Atomic 객체 사용에 주의해야 한다.

profile
개인 공부를 정리함니다

0개의 댓글