프로그래밍/C,C++,C#

[C/C++] volatile 이란(쓰레드/변수 값 안바뀌는 오류)

Beginner:) 2022. 4. 26.
320x100

volatile이란 컴파일러 최적화에 관계없이 메모리에 접근하여 데이터값을 확인하는 예약어이다.

 

무슨말이냐 하면 

 

보틍 프로그래밍을 할 때 Debug모드로 할 것이다.

 

Debug모드를 한 상태로 10억번의 반복문을 도는 아래의 코드를 실행해본다.

 

#include <stdio.h>
#include <time.h>

int main(void)
{
	clock_t start = 0, end = 0;
	float res = 0;
	
	start = clock();

	for (int i = 0; i < 1000000000; i++);

	end = clock();

	res = (end - start * 1.0) / CLOCKS_PER_SEC;
	printf("time = %.3f", res);
	return 0;
}

 

실행을 하게 되면 약 2초의 시간이 걸리는 것을 알 수 있다.

 

 

소스코드는 그대로 두고 Debug에서 Release 모드로 변경해본다.

 

실행을 해보면 Debug모드에서 2초였던 것이 0초가 실행되는 것을 볼 수 있다.

 

 

Release 모드에서는 배포판이 되기에 컴파일러 최적화가 일어난다. 

 

예를 들어 위의 코드는 의미없는 반복문,

아래의 코드에서 num의 값은 3이 최종이기에 1, 2를 대입하는 코드는 무시된다.

 

int num = 0;
num = 1;
num = 2;
num = 3;

 

하지만 중요한 것은 컴파일러 최적화가 만능은 아니다.

 

예를 들어 아래의 코드를 보면 전역변수인 value를 기준으로 쓰레드에서 while문을 반복하고 있다.

 

쓰레드를 시작하고 1초 뒤 value의 값을 0으로 만들어주면 while반복문이 멈춰야하는것이 정상이지만 멈추지 않는다.

(Release 모드)

 

#include <stdio.h>
#include <Windows.h>
#include <process.h>


int value = 1;
unsigned _stdcall Thread_A(void* arg){
	value = 1;
	while (1 == value);
	printf("The End\n");
	return 0;
}

int main(void){
	
	HANDLE hand = (HANDLE)_beginthreadex(NULL, 0, Thread_A, 0, 0, 0);
	Sleep(1000);
	value = 0;
	
	WaitForSingleObject(hand, INFINITE);

	return 0;
}

 

Thread_A함수에서 value=1이라고 초기화를 해주었고 컴파일러는 while문에서 value의 상태를 변경하는 값이 없으니 최적화를 실행하여 value의 메모리값을 읽어보지 않는다.

 

그 뒤 다른 쓰레드/포그라운드에서 value의 값을 변경해주어도 컴파일러는 최적화를 했기 때문에 value의 메모리값을 읽지 않는다.

 

그래서 value 변수의 메모리값을 강제로 읽게하는 키워드가 volatile이다.

 

전역변수 value의 타입 앞에 volatile을 선언하면 value의 실제 메모리값을 읽는다.

 

자주 일어나는 오류는 아니지만 치명적이기에 알아는 두자.

 

 

반응형

댓글