1. Memory Order
1.1 atomic
- atomic 객체들의 경우 원자적 연산 시에 메모리 접근할 때 어떠한 방식으로 접근하는지 지정할 수 있음
1.2 memory_order_relexed
- 가장 느슨한 조건
- 메모리에서 읽거나 쓸경우
- 주위의 다른 메모리 접근들과 순서가 바뀌어도 무방
1.2.1 Main
- 2가지의 atomic 객체 생성 후 쓰레드 생성
int main()
{
vector<std::thread> workers;
std::atomic<int> number1(0);
std::atomic<int> number2(0);
workers.push_back(std::thread(worker1, std::ref(number1), std::ref(number2)));
workers.push_back(std::thread(worker2, std::ref(number1), std::ref(number2)));
for (int i = 0; i < 2; i++) workers[i].join();
cout << number1 << number2 << endl;
}
1.2.2 worker
- store, load
- worker1의 경우
- number2 = 1;
- number = number1;
- worker2의 경우
- number1 = 1;
- number= number2;
- memory_order_relaxed
- 느슨한 방식 사용
- 결과 값 (0, 1) (1, 0) (1, 1) 생성 가능
- 만약 (0, 0)이 나오는 케이스를 생각해 본다면 나올 수 없음
- 값을 넣는 단계에서 둘다 0이 나올 수 있는 경우의 수가 없음
- 하지만 느슨한 방식을 사용하면서 메모리 연산들 사이에서 어떠한 제약조건이 없으므로 순서변경 가능
- 읽기를 먼저하게 되면 (0, 0)이 나오게 됨
- 이에 따라 CPU에서 매우 빠른 속도를 실행
void worker1(std::atomic<int>& number1, std::atomic<int>& number2)
{
number2.store(1, memory_order_relaxed); // 쓰기
int number = number1.load(memory_order_relaxed); // 읽기
cout << number << endl;
}
void worker2(std::atomic<int>& number1, std::atomic<int>& number2)
{
number1.store(1, memory_order_relaxed); // 쓰기
int number = number2.load(memory_order_relaxed); // 읽기
cout << number << endl;
}
1.3 memory_order_release
1.3.1 memory_order_relaxed
- CPU에 많은 자유를 부여하기 때문에 사용용도는 제한적
- 먼저 실행하고 후에 해야할 일을 먼저 실행할 수 있음
- memory_order_release 및 memory_order_acquire 사용
- 밑의 프로그램을 실행하면 순서가 재배치되면서 number2가 0이 나올 수 있음
void worker1(std::atomic<int>& number1, std::atomic<int>& number2)
{
number2 = 2;
number1.store(1, memory_order_relaxed);
}
void worker2(std::atomic<int>& number1, std::atomic<int>& number2)
{
while (!number1.load(memory_order_relaxed)) {}
cout << "number2" << endl;
}
1.3.2. memory_order_release
- 해당 명령 이전의 모든 메모리 명령들이 해당 명령 이후로 재배치 되는 것을 금지
void worker1(std::atomic<int>& number1, std::atomic<int>& number2)
{
number2.store(2, memory_order_release);
number1.store(1, memory_order_release);
}
1.3.3 memory_order_acquire
- 해당 명령 뒤에 오는 모든 메모리 명령들이 해당 명령 위로 재배치 되는 것을 금지
- 이 메모리 순서를 사용하는 로드 작업은 영향을 받는 메모리 위치에서 획득 작업 을 수행
- 현재 스레드에서 읽기 또는 쓰기는 이 로드 전에 재정렬될 수 없음
void worker2(std::atomic<int>& number1, std::atomic<int>& number2)
{
while (number1.load(memory_order_acquire)) {}
cout << number2 << endl;
}
→ 다른 쓰레드에서 같은 변수를 사용할 때 memory_order_release & memory_order_acquire를 통해 동기화를 수행
1.3.4 memory_order_acq_rel
- acquire & release를 모두 수행하는 것
1.3.5 memory_order_seq_cst
- 메모리 명령의 순차적 일관성을 보장
- 메모리 명령 재배치X
- 모든 쓰레드에서 모든 시점에 동일하 값을 관찰
- CPU가 동작하는 방식
- 멀티 코어 시스템에서 꽤나 비싼 연산
'Create Game > [Window API] Game Client & Game Server' 카테고리의 다른 글
[SEVER] UDP Server (0) | 2023.01.10 |
---|---|
[SEVER] TCP Server (0) | 2023.01.10 |
[SERVER] Atomic (0) | 2023.01.05 |
[SERVER] Condition Variable (0) | 2023.01.03 |
[SERVER] Producer & Consumer (0) | 2023.01.03 |