전 글에서 프로세스와 쓰레드의 차이점을 정리해보았다.
예제 코드를 작성해보자
#include "pch.h"
#include <iostream>
#include <thread>
static int tempNum = 0;
void Add()
{
for (int i = 0; i < 1000000; i++)
{
tempNum++;
}
}
void Sub()
{
for (int i = 0; i < 1000000; i++)
{
tempNum--;
}
}
int main()
{
cout << tempNum << endl;
std::thread t1(Add);
std::thread t2(Sub);
t1.join();
t2.join();
cout << tempNum << endl;
}
static int tempNum = 0;
//tempNum이라는 전역변수를 0으로 초기화하고
cout << tempNum << endl;
//0으로 초기화된 tempNum 출력
std::thread t1(Add);
std::thread t2(Sub);
//tempNum을 백만번 증가시키는 쓰레드를 실행
//tempNum을 백만번 감소시키는 쓰레드를 실행
t1.join();
t2.join();
//t1 쓰레드가 종료될때까지 대기
//t2 쓰레드가 종료될때까지 대기
cout << tempNum << endl
쓰레드 실행이 완료된 상태에서 tempNum출력
결과는 어떻게 나올까?
0으로초기화하고
백만번더하면서 백만번을빼니까 정답은 0??
실제로 결과값은 0이 나오지않는다.
심지어 실행할때마다 결과값은 다르게 출력된다.
왜 이렇게될까?
실제로 백만번 1씩 증가시키는 부분의 어셈블리코드는 보면 위와같다
00007FF6EB922515 mov eax,dword ptr [tempNum (07FF6EB92F440h)]
//07FF6EB92F440h 의 주소에있는 데이터를 eax로 이동
00007FF6EB92251B inc eax
//eax값을 증가
00007FF6EB92251D mov dword ptr [tempNum (07FF6EB92F440h)],eax
//증가된 eax값을 다시 07FF6EB92F440h주소로 이동
우리가 코드를 작성할때에는 한줄로 tempNum++ 한줄로 표현하지만
실제로 코드가 돌아갈때에는
메모리에서 -> CPU로 값을 이동하고 -> 연산을 한 후에 -> 다시 CPU로 이동을 하는 행위를한다.
그럼 다시 쓰레드가 돌아가는 상황을 생각해보자
tempNum은 0이다.
Add thread> tempNum에 있는 데이터를 CPU a위치로 옮겼다. 옮겨진 값은 0이다.
Sub thread> tempNum에있는 데이터를 CPU b위치로 옮겼다. 옮겨진 값은 0이다.
Add thread> a위치의 값을 +1했다 연산된 값은 +1이다
Sub thread> b위치의 값을 -1했다 연산된 값은 -1이다
Add thread> +1된 데이터를 다시 tempNum 메모리 위치로 옮긴다. tempNum은 +1이다
Sub thread> -된 데이터를 다시 tempNum 메모리 위치로 옮긴다. tempNum은 -1이된다.
위 스탭은 예시이다 실제로 저렇게 드라마틱하게 한단계씩 실행되지는않는다.
위 예시에서 중요한점은
Add Thread에서 연산된값은 Sub쓰레드에서 연산된 값으로 덮혔다는것이다.
위와같은 행위가 계속 반복되면서
백만번 더하고 백만번 빼는것같지만 0이라는 결과값이 나오지 않는것이다.
위와같은 행위를 해결할 수 있는 방법이 동기화이다.
다음글에서 설명이어서 하겠습니다.
'게임 프로그래밍 > WINDOW SERVER' 카테고리의 다른 글
Atomic (0) | 2025.02.25 |
---|---|
프로세스? 쓰레드? (0) | 2025.02.25 |