아래 코드를 실행 하면 이코드는 싱글 스레드에선 실행되지 않는 코드 이지만
코드 재배치 또는 메모리 가시성 문제로 인하여 멀티 스레드 환경에서
실행 되는 상황이 발생 될 수도 또는 발생 되지 않을 수도 있다
아래 코드가 종료 되는 이유는 크게 두가지가 있다
먼저 알아야 햐는 부분이 각 코어마다 별도의 캐쉬를 갖고 있는다
(https://3dmpengines.tistory.com/2194)
문제가 될만한 가능성은 두가지이다
- 가시성 (https://3dmpengines.tistory.com/2195)
= 캐쉬와 램 사이에 데이터가 보이는가
cpu 가 어떤 값을 쓰거나 읽을때 램까지 가서 읽어올수도 있는데
이것은 캐쉬에 없을때 얘기다 , 이때 CPU 는 각 코어마다 별도의 캐쉬를 갖고 있는데
실제 램까시 가서 데이터를 불러와서 변수에 쓴건지에 대한 보장이 없을 수 있다
코어와 스레드는(t1) 하나로 묶여서 연산 되고 cpu 에서 캐쉬의 데이터를 연산을 하면서 아직 Ram 에 데이터를 쓰지 않았을 수 있는데
이때 다른 쓰레드(t2)에서 현재 t1 에서 아직 공유변수의 연산중인 값의 최종을 모르고 이전 램에 있는 값만 알기 때문에 값이 정확하게 일치하지 않는다는 문제가 비가시성을 말함 - 코드 재배치 (https://3dmpengines.tistory.com/2196)
= 결과가 동일하다면, 컴파일러가 코들르 보고 더 빠를 것 같으면 코드 순서를 바꿀 수도 있다
이것 때문에 값이 제대로 원하는 값이 아닐 수도 있다, 코드로 보는것과 달리
그런데 이 코드 재배치는 컴파일러 뿐만 아니라 CPU 에서 멋대로 제배치 할 수도 있다
과연 컴파일러만 재배치를 할까?
한 가지 더 재미있는 점은, 꼭 컴파일러만이 명령어를 재배치하는게 아니라는 점입니다. 예를 들어서 다음과 같은 두 명령을 생각해봅시다.
C/C++ 확대 축소
// 현재 a = 0, b = 0;
a = 1; // 캐시에 없음
b = 1; // 캐시에 있음
a = 1 의 경우 현재 a 가 캐시에 없으므로, 매우 오래 걸립니다. 반면에 b = 1; 의 경우 현재 b 가 캐시에 있기 때문에 빠르게 처리할 수 있겠지요. 따라서 CPU 에서 위 코드가 실행될 때, b = 1; 가 a = 1; 보다 먼저 실행될 수 있습니다.
따라서, 다른 쓰레드에서 a 는 0 인데, b 가 1 인 순간을 관찰할 수 있다는 것입니다.
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <future>
#include "windows.h"
using namespace std;
int32 x = 0;
int32 y = 0;
int32 r1 = 0;
int32 r2 = 0;
volatile bool ready;
void Thread_1()
{
while (!ready)
{
volatile int i = 39;
i += 10;
}
//가시성 관점으로 봤을때 특정 시점에서 이것 또한 램까지 가서
//y 에 데이터를 썼다는 보장이 없을 수 있다
y = 1; //store y
r1 = x; //load x
}
void Thread_2()
{
while (!ready)
{
volatile int i = 39;
i += 10;
}
x = 1;
//가시성 문제로 봤을때 특정 시점에서 y 는 1이 아닐 수도 있다
r2 = y;
}
int main()
{
int32 count = 0;
while (true)
{
ready = false;
count++;
x = y = r1 = r2 = 0;
thread t1(Thread_1);
thread t2(Thread_2);
ready = true;
t1.join();
t2.join();
if (r1 == 0 && r2 == 0)
{
break;
}
}
std::cout << count << std::endl;
return 0;
}
ready 하단에 들어간 volatile 은 테스트를 위해 의미 없이 넣은 코드조각이다
반응형
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
atomic 예시 와 Memory Model (정책) , memory_order_... (0) | 2022.09.12 |
---|---|
원자적 연산과 동일한객체 동일한 수정 순서 (0) | 2022.09.12 |
CPU 파이프라인과 코드 재배치 (0) | 2022.09.12 |
Cache (3) 코어와 캐시 메모리 그리고 가시성 (0) | 2022.09.12 |
Cache (2) - 코어당 캐시메모리 (0) | 2022.09.12 |