뽕구의 개발일지

[C#] Thread 쓰레드 - 종료 방법 본문

카테고리 없음

[C#] Thread 쓰레드 - 종료 방법

뽕구 2025. 4. 24. 21:05
728x90
반응형

 

Thread 기초에 대해서 이전에 포스팅했습니다.

글을 읽다보니, 신입때 쓰레드를 종료시키는데 애먹었던 기억이 났어요.

 

그리고 필수로 다루어야 할 내용이기도 한 것 같습니다.

그리고 참고로 썸네일을 바꾸었어요 좀 더 깔끔한것 같네요 :)

 

이번에는 쓰레드를 종료시키는 방법에 대해서 포스팅하겠습니다.

쓰레드 종료시키기

가장 안전한 방법은 쓰레드가 실행하는 메서드를 다 실행하면 자동으로 종료가 됩니다.

  • for, while 등 반복문이 종료 조건이 되어 끝나는 경우
  • return을 만나는 경우

별도로 종료를 시킬 필요가 없다면 일 끝나면 알아서 종료되게 냅두는게 가장 좋아요

그치만 개발하다보면 분명히 쓰레드를 중지시켜야할 때가 있습니다.

 

안전한 방법과 불안전한 방법에 대해 알아보겠습니다.

불안전한 종료 방법

먼저 불안전한 종료 방법부터 알아봐요.

 

Thread.Abort() 라는 메서드가 있습니다.

Visual Studio 2022에서는 쓰지말라고 함수에 삭선을 그어버리네요 이제 ㅋㅋㅋㅋㅋㅋㅋ

 

과거에는 쓰레드를 종료시킬 때 이 Abort()를 호출해서 강제로 종료시키곤 했습니다.

당연히 강제 종료니까 리소스, 데이터 처리, 상태 관리 등 문제가 생길 수 있어요.

 

회사에서 개발할 때 .NET Framework 4.5 완전 구버전을 써서 몰랐는데 .NET 5 이상에서는 Abort()를 지원 안한다고 하네요.

아 이런 구시대 유물이 있었구나~ 생각하시고 쓰지 맙시다

 

안전 종료하는 방법

대표적으로 두가지가 있습니다.

종류 방법
Flag 변수 사용 반복문에서 Flag 변수 변화를 계속 체크하다가
Falg 변수 변화 시 반복문 빠져
CancellationToken 사용 Flag 변수랑 비슷하게 사용됨.
반복문에서 token의 종료 요청을 계속 체크하다가
token 종료 요청 확인 시 반복문 빠져나옴 

 

Flag 변수 사용법

저는 업무할 때 Flag 방식을 가장 많이 썼었어요.

보편적으로 bool 타입을 가장 많이 쓰고, 저도 가장 많이 썼어요.

 

상황에 따라 특정 정수값을 할 때도 있겠지만 값 비교는 상위 코드에서 하고 bool 타입으로 체크 가장 빠를 것 같아요.

Flag 변수 사용 코드 한번 작성해봤어요.

 

코드 요약하면 순서는 아래와 같습니다.

  • Flag 변수 flagStop 선언 (bool 타입)
  • Dowork() 메서드 내부의 while에서 Flag 변수의 상태가 true로 변할때 까지 반복.
  • RequestStop() 메서드에서 Flag 변수에 true 대입
  • 쓰레드 종료

최종적으로 Dowork() 메서드의 while 조건이 false가 되어 빠져나오고, 메서드는 자연스럽게 종료되는 구조입니다.

간단하게 단계별로 콘솔 출력물 찍어보면 언제 종료 요청이 들어가고 종료가 되는지 알 수 있어요.

using System;
using System.Threading;

class Program
{
    // Flag 변수를 사용하여 쓰레드 종료 요청하기
    static bool flagStop = false;

    static void DoWork()
    {
        int count = 0;

        // flagStop 변수가 true가 될 때까지 반복
        while (!flagStop)
        {
            Console.WriteLine($"아직 Flag 안떴어! 카운트 : {count++}");
            Thread.Sleep(500);
        }

        Console.WriteLine("쓰레드 종료!");
    }

    // flagStop 변수를 true로 변경하는 메서드
    static void RequestStop()
    {
        //flag 변수 true 대입
        flagStop = true;

        Console.WriteLine("Flag 변수 true 변경!");
        Console.WriteLine("쓰레드 종료 요청!");
    }

    public static void Main()
    {
        Thread t1 = new Thread(new ThreadStart(DoWork));
        t1.Start();

        // 메인 쓰레드 동작시키면서 호출 지연시키기
        for (int i = 0; i < int.MaxValue; i++)
        {
            if (i == int.MaxValue - 1)
            {
                // flagStop 변수를 true로 변경하는 RequestStop 메서드 호출
                RequestStop();
            }
        }
    }
}

 

 

Flag 변수로 Thread 종료처리 결과

 

응용 방법도 참 많을테니 각자 환경에 맞게 잘 사용해보면 될 것 같습니다.

 

추가로, 다중 쓰레드인 경우

volatile 키워드를 사용해 컴파일러에게 Flag 변수를 최적화하지 말라고 하거나, 

lock 키워드를 써서 다중 접근을 막아주어서

flag 변수에 대한 다중 접근을 방어해줘야 해요.

 

이를 쓰레드 동기화 라고도 하는데, 동기화가 안되면 쓰레드가 CPU 캐시에서 값을 읽어올 때 값이 다른 일관성 문제가 생길 수 있어요.

A쓰레드가 값을 바꿨는데 B 쓰레드는 이전 값이 읽힌다는 얘기에요.

이 내용은 매우 중요하니 추후 데드락, 동기화와 같은 포스팅에서 다시 다루겠습니다.

 

CancellationToken  토근 사용법

다음으로 CancellcationToken 으로 종료시키는 방법에 대해서 알아보겠습니다.

.NET에서 사용하는 방식으로 토근을 사용해 종료 요청을 하게 됩니다.

아래 예시 코드를 작성해봤습니다.

  • 쓰레드 메서드의 파라미터를 CancellcationToken 타입으로 선언
  • CancellationTokenSource 토큰 tokenSource를 생성하여 쓰레드 초기화 시 파라미터로 전달
  • 쓰레드 시작
  • 종료가 필요한 시점에 tokenSource.Cancel() 호출
  • token의 isCancellationRequested 속성값 true로 변경
  • 쓰레드 종료

 

using System;
using System.Threading;

class Program
{
    //CancellationTokenSource 생성
    public static CancellationTokenSource tokenSource = new CancellationTokenSource();

    //토큰으로 쓰레드 종료 요청 처리위해 메서드에 CancellationToken 매개변수 전달
    static Thread t1 = new Thread(() => DoWork(tokenSource.Token));

    // CancellationToken을 사용하여 쓰레드 종료 요청을 처리하는 메서드
    static void DoWork(CancellationToken token)
    {
        int count = 0;

        // CancellationToken이 요청된 상태가 아닐 때까지 반복
        while (!token.IsCancellationRequested)
        {
            Console.WriteLine($"아직 Token요청 안떴어! 카운트 : {count++}");
            Thread.Sleep(500);
        }

        Console.WriteLine("쓰레드 종료!");
    }

    // CancellationTokenSource를 사용하여 쓰레드 종료 요청을 처리하는 메서드
    static void RequestStop()
    {
        //flag 변수 true 대입
        tokenSource.Cancel();

        Console.WriteLine("TokenSource 취소 요청!");
        Console.WriteLine("쓰레드 종료 요청!");
    }

    public static void Main()
    {
        t1.Start();

        // 메인 쓰레드 동작시키면서 호출 지연시키기
        for (int i = 0; i < int.MaxValue; i++)
        {
            if (i == int.MaxValue - 1)
            {
                // flagStop 변수를 true로 변경하는 RequestStop 메서드 호출
                RequestStop();
            }
        }
    }
}

 

token을 사용해서 Thread를 종료 처리 결과에요.

Token으로 Thread 종료처리 결과

 

취소요청에 대한 속성 IsCancellationRequested는 bool 타입입니다. 초기값은 false이겠네요.

 

 

그리고 Token의 IsCancellationRequested 속성 값이 true로 변경된 것을 확인할 수 있습니다.

이어서 while문을 빠져나오고 "쓰레드 종료!" 프린트 문이 수행됩니다.

IsCancellationRequested true 변경 후 while 종료

 
 
 

그럼 Flag와 Token 뭘 써야하나?

단순한 처리일때는 Flag 변수 방식 사용하고 Token 방식 을 쓰는게 좋은 선택같습니다.

CancellationToken은 내부적으로 동기화 처리를 안전하게 해준다고 합니다.

따라서 메모리 일관성같은 문제를 개발자가 고민하지 않아도 되며

 

.NET 환경에서도 이를 공식적으로 지원을 하고 있는 부분이기도 하기에, 

되도록 Token 방식을 사용하는게 나아 보이네요~

 

마치며..

간단한 예제로 Thread를 종료시키는 방법에 대해 알아보았습니다.

이미 오래전에 나온 것 이지만 최신 C#과 .NET 버전에 편리한 기능들이 많아지는 것 같네요. 

 

회사에서 구버전 프로젝트들이 자리를 잡고 있어서 신버전을 못써본게 아쉽네요.

앞으로 계속 공부하면서 글 올리도록 하겠습니다.

 

읽어주셔서 감사합니다!

728x90
반응형