CPU 파이프라인
- Fetch : 명령어를 가져온다
- Decode : 명령어를 해석한다
- Execute : 명령어를 실행한다
- Write back : 결과를 저장한다
CPU 파이프라인은 위와 같은데 유념해야 할 것은 CPU는 명령어를 받은 순서대로 처리한다는 보장이 없다
어떤 연산들의 경우 CPU가 판단해서 순서를 뒤바꾼 다음 처리하기도 한다
전역으로 a, b, r1, r2 int형 변수를 두고, 2개의 쓰레드를 만든 후 r1, r2가 0이 되지 않는 환경을 구성해서 실행했으나
위에서 언급한대로 CPU가 순서를 뒤바꾼다음 처리하여 r1, r2가 0이 되는 현상이 나타났다.
CPU 뿐만 아니라 컴파일러에서도 안쓰이는 변수들이나 의미없어'보이는' 변수들의 경우 생략하는 식의 최적화를 진행하는데 volatile이라는 키워드를 사용하면 이런 최적화를 하지 말아달라고 전달할 수 있다.
이렇듯 컴파일러나 CPU에서 최적화하는 부분들을 주의해야 한다!
a += 1;
이 단순한 연산은 어셈블리로 아래와 같이 표현된다.
eax = a;
eax = eax + 1;
a = eax;
C++에서 간단한 한 줄짜리 연산도 어셈블리에선 3줄로 표현된다.
이런 현상으로 일어날 수 있는 문제들을 짚어볼 수 있다.
#include <iostream>
#include <thread>
#include <vector>
#include <windows.h>
using namespace std;
int sum = 0;
void Add()
{
for (int i = 0; i < 100'0000; ++i)
{
sum++;
}
}
void Sub()
{
for (int i = 0; i < 100'0000; ++i)
{
sum--;
}
}
int main()
{
//ios_base::sync_with_stdio(false);
//cin.tie(NULL);
//cout.tie(NULL);
thread t1(Add);
thread t2(Sub);
t1.join();
t2.join();
cout << sum << endl;
return 0;
}
- 이 코드의 마지막에서 sum을 출력하는 부분에선 0이 출력되어야 하지만 -764313 또는 970031과 같은 말도 안되는 숫자들이 출력되었다.
- 그 이유는 eax에 값을 가져오는 과정에서 다른 스레드와의 동기화가 이루어지지 않기 때문이다.
(첫 번째 스레드의 마지막에 a = eax 부분이 실행되기 전에 두 번째 스레드에서 eax = a가 실행되게 되면 첫 번째 스레드에서 이루어진 연산은 무시된 채 두 번째 스레드의 결과값이 덮어써지게 된다)
Atomic
그래서 사용하는 것이 Atomic
atom이라는 것은 '원자'라는 뜻으로 물리에서는 더 이상 쪼개질 수 없는 것을 의미한다.
#include <iostream>
#include <thread>
#include <vector>
#include <windows.h>
#include <atomic>
using namespace std;
atomic<int> sum = 0;
void Add()
{
for (int i = 0; i < 100'0000; ++i)
{
sum++;
}
}
void Sub()
{
for (int i = 0; i < 100'0000; ++i)
{
sum--;
}
}
int main()
{
//ios_base::sync_with_stdio(false);
//cin.tie(NULL);
//cout.tie(NULL);
thread t1(Add);
thread t2(Sub);
t1.join();
t2.join();
cout << sum << endl;
return 0;
}
- atomic<int>를 사용하면 공유자원에서 여러 스레드가 접근하더라도 값이 덮어써지는 문제를 해결할 수 있다.
- 다만 이 atomic<int>로 하는 연산은 일반 정수 연산에 비해 10배정도 느리기 때문에 공유자원에 한해서만 사용하는 것이 좋다.
- 예를 들어 멀티스레드 환경에서 플레이어의 수를 계산할 때 atomic<int>를 사용하면 된다.
'게임 서버 > [Inflearn_rookiss]올인원_클라&서버 연동' 카테고리의 다른 글
6. 데드 락 (0) | 2023.12.11 |
---|---|
5. 스핀 락 (0) | 2023.12.11 |
4. Lock 기초 (0) | 2023.12.11 |
2. 멀티 쓰레드 실습 (0) | 2023.12.11 |
1. 멀티 쓰레드란? (0) | 2023.12.11 |
댓글