반응형

 

아래 코드를 실행 하면 이코드는 싱글 스레드에선 실행되지 않는 코드 이지만

코드 재배치 또는 메모리 가시성 문제로 인하여 멀티 스레드 환경에서

실행 되는 상황이 발생 될 수도 또는 발생 되지 않을 수도 있다

 

아래 코드가 종료 되는 이유는 크게 두가지가 있다

 

먼저 알아야 햐는 부분이 각 코어마다 별도의 캐쉬를 갖고 있는다
(https://3dmpengines.tistory.com/2194)


문제가 될만한 가능성은 두가지이다

  1. 가시성 (https://3dmpengines.tistory.com/2195)
    = 캐쉬와 램 사이에 데이터가 보이는가

    cpu 가 어떤 값을 쓰거나 읽을때 램까지 가서 읽어올수도 있는데
    이것은 캐쉬에 없을때 얘기다 , 이때 CPU 는 각 코어마다 별도의 캐쉬를 갖고 있는데 
    실제 램까시 가서 데이터를 불러와서 변수에 쓴건지에 대한 보장이 없을 수 있다
    코어와 스레드는(t1) 하나로 묶여서 연산 되고 cpu 에서 캐쉬의 데이터를 연산을 하면서 아직 Ram 에 데이터를 쓰지 않았을 수 있는데
    이때 다른 쓰레드(t2)에서 현재 t1 에서 아직  공유변수의 연산중인 값의 최종을 모르고 이전 램에 있는 값만 알기 때문에 값이 정확하게 일치하지 않는다는 문제가 비가시성을 말함

  2. 코드 재배치 (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 은 테스트를 위해 의미 없이 넣은 코드조각이다

 

ref :https://modoocode.com/271

반응형

+ Recent posts