릴리즈 모드 시 발생하는 코드 최적화에 의한 오류를 공부해보자
아래 코드는 멀티 쓰레드 환경에서 주 쓰레드를 1초 동안 멈추었다가 다시 동작하게 하여 다른 쓰레드를 종료하게 만드는 코드이다.
class Program
{
static bool _stop = false;
static void ThreadMain()
{
Console.WriteLine("쓰레드 시작");
while(_stop == false)
{
// 누군가가 stop 신호를 주기를 기다린다.
}
Console.WriteLine("쓰레드 종료");
}
static void Main(string[] args)
{
Task t = new Task(ThreadMain);
t.Start();
Thread.Sleep(1000); // 밀리세컨드 단위 동안 잠든다 (대기)
Console.WriteLine("1초가 지났습니다.");
_stop = true;
Console.WriteLine("Stop 호출");
Console.WriteLine("종료 대기 중");
t.Wait(); // <==> Thread.Join()
Console.WriteLine("종료 성공");
}
}
1초가 지난 후에 _stop이 true로 변경되어 Task(Mainthread)가 종료되고 이어서 주 쓰레드 역시 종료된다.
하지만 Debug 모드에서와 달리, Release 모드에서는 동작이 달라지게 된다.
stop이 호출되었다는 뜻은 _stop이 true로 변경되었다는 뜻인데 Task가 종료되지 않는 것이 이상하다.
이는 Release 모드 시, 코드 최적화가 일어나서
while(_stop == false) {} 부분이
if(_stop == false) {
while(true){}
}
처럼 동작하기 때문이다.
컴파일러는 while의 본문에 _stop을 변경하는 코드가 없는 것을 고려하여 코드를 최적화 하였지만, 멀티 쓰레드 환경임을 고려하짐 못하여 위와 같은 에러가 발생하는 것이다.
이를 막기 위해서는 Release 모드 시 최적화를 하지 말라는 것을 알려주는 키워드가 필요하다.
volatile static bool _stop = false 라고 처음에 선언한다.
volatile 키워드를 붙이면 Release 모드 시 최적화를 하지 말라고 컴파일러에게 알려줄 수 있다.
하지만 volatile은 C#에서 기구한 동작을 하기 때문에 C# 전문가들이 사용하지 말라고 권고하고 있다.