포스트

C# 릴리즈 모드 에러, (컴파일러 최적화)

싱글쓰레드 프로그래밍과는 다르게, 멀티쓰레드 프로그래밍은 분명히 디버그 모드일때는 잘 동작하던 코드가 릴리즈 모드일때는 여러 에러를 만들어내는 경우가 많다고 합니다. 그리고 그 원인은 바로 Release를 위해 컴퓨터가 assembly 언어로 변환할 때 자체적으로 하는 코드 최적화 때문일 수 있습니다. 릴리즈를 해본 경험이 풍부하지 않다면 미리 많은 에러를 예상하기는 어렵고, 멀티쓰레드 프로그래밍은 특히 릴리즈를 할 때 에러가 나지 않도록 주의깊게 처리해야 한다고 합니다. 그렇다면 이제 그러한 에러 사례 중 한 가지를 실습해보겠습니다.


먼저 아래와 같이 Task()메소드를 사용해서 멀티쓰레드를 구현해줍시다. Task()메소드의 Wait()함수는 지난 포스팅에서 다루었던 Thread()메소드의 Join()함수와 같은 역할을 합니다. 바로 멀티쓰레드가 종료될 때까지 기다려주는 함수입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
using System.Data;
using System.Threading;

namespace ServerCore
{
    internal 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);

            _stop = true;

            Console.WriteLine("Stop 호출");
            Console.WriteLine("종료 대기중");

            t.Wait();
            Console.WriteLine("종료 성공");

        }
    }
}

이제 F5를 눌러서 실행해줍시다.

잘 실행이 되었고 메인쓰레드도 잘 종료되었습니다. 그러면 이제는 Release모드로 실행을 해봅시다.

아까와는 다르게 메인쓰레드가 종료 되고 있지 않습니다. while문에 갇혀있습니다. 왜 그럴까요?


디버그 모드에서는 잘 동작하던 쓰레드가 왜 릴리즈 모드에서는 제대로 동작하지 않았을까요? 그 원인은 바로 릴리즈 모드로 실행하면서 컴퓨터가 기계 어셈블리어로 변환하는 도중에 최적화를 해서 코드를 변형했기 때문입니다. 컴퓨터는 우리가 만든 ThreadMain함수를 볼때 우리가 일부러 멀티쓰레드를 실행할 용도로 함수를 만든 것을 모릅니다. 그래서 while문에 _stop 플래그가 true로 바뀌는 경우가 while문 내에 보이지 않으므로,

1
while(_stop == false){ }

컴퓨터는 위 코드를 최적화를 위해 다음과 같이 바꾸게 됩니다.

1
if(_stop == false){ while(true){ }}

그러므로 while문을 빠져나오지 못하게 되었던 것입니다.

이를 해결하는 방법에는 여러가지가 있는데, 가장 간단히 테스트해볼 수 있는 방법은 _stop변수를 volatile(휘발성) 변수로 지정해주는 것입니다. (이는 임시방편으로, 권장되는 방법은 아니라고 합니다.)

1
volatile static bool _stop = false;

다시 Relase모드로 실행해볼까요?

정상적으로 실행 되었습니다.


이처럼 멀티쓰레드 프로그래밍한 코드는 릴리즈할 때 전혀 생각지도 못했던 경우에서 에러가 발생하는 경우가 많다고 합니다. 그리고 그 원인이 되는 어셈블리어는 저는 얼핏 보았는데 꽤 어려워보였습니다. 그래서 멀티쓰레드 프로그래밍을 잘 하는 것이 프로그래밍계의 흑마법을 부리는 것이라는 비유가 있었던 것 아닐까 하고 이해해봅니다.


이 글은 인프런의 '[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버'를 듣고 개인적으로 정리하기 위해 작성하였습니다. 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.