[C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - Atomic

Jangmanbo·2023년 7월 9일
0

멀티쓰레드 환경에서 공유 데이터 사용 시 문제점

메인쓰레드

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줄의 코드가 실행되기 때문에, AddSub가 3줄의 코드를 한 번에 순차적으로 실행한다는 보장이 없기 때문에 공유데이터의 결과가 0이 아닌 것이다.

Atomic

Atomic, 번역하면 원자.
AddSub의 코드는 모두 실행되거나, 모두 실행되지 않아야 한다.(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 타입이면 멀티쓰레드 환경에서 만약 Addsum++가 먼저 실행됐을 경우 sum++가 모두 끝난 후 Subsum--가 실행된다.
따라서 정상적으로 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;	// 멀티스레드 환경에서는 오답
}

0개의 댓글