본문 바로가기

.NET/C#

[C#] Thread가 백그라운드에서 살아있을 때

3줄 요약

  • Thread.IsBackgroud 를 true로 하면 응용프로그램이 종료될 때 스레드가 함께 종료된다.
  • Thread.Abort()는 사용하지 말자.
  • new Thread( myThreadFunc ) { IsBackgroud = true }.Start(); 로 스레드 한 줄 실행 가능.

 

일정한 동작을 반복하는 무한 루프 형태의 Thread는 응용프로그램이 종료될 때 함께 종료되어야 한다.

그래서 보통 아래와 같이 스레드에서 무한정 도는 루프를 스레드 외부에서 탈출시키기 위한 조건 변수를 매개로 동작을 제어하는 방법을 사용한다.

private void button_Start_Click( object sender, EventArgs e )
{
    new Thread(loop).Start();
}

private void button_Stop_Click( object sender, EventArgs e )
{
    _isRun = false;
}

private bool _isRun;
private void loop()
{
    _isRun = true;
    
    while ( _isRun )
    {
        // Do something
    }
}

 

혹은 아래와 같이 Thread.Abort() 메서드를 호출해서 스레드 내부에서 예외를 강제로 발생시켜 스레드를 중단시키기도 한다.

private void button_Start_Click( object sender, EventArgs e )
{
    if ( _thread != null )
    {
        _thread = new Thread(loop);
        _thread.Start();
    }
}

private void button_Stop_Click( object sender, EventArgs e )
{
    if ( _thread != null )
    {
        _thread.Abort();
        _thread = null;
    }
}

private Thread _thread;
private void loop()
{
    while ( true )
    {
        // Do something
    }
}

그러나 .NET 공식 학습서에서는 Thread.Abort()를 주의해서 사용해야 할 것임을 권고하며 애초에 메서드에 대한 설명을 이렇게 적어놨다.

https://learn.microsoft.com/ko-kr/dotnet/api/system.threading.thread.abort?view=net-7.0

 

예외를 발생시켜 스레드를 종료시키는 것이며, '대개' 스레드가 종료된다고 명시되어 있고, Thread.Join()으로 스레드의 종료를 대기하여도 대기 상태가 종료되는것 또한 보장하지 않는다.

 

확실히 Abort()를 쓰는 것 보다는 루프 탈출 변수를 사용하는 것이 바람직해 보인다.

따라서 프로그램이 종료되는 시점에 아래와 같은 처리를 하여 스레드를 종료할 수 있다.

// 앞의 코드 중략

private void form_FormClosing( object sender, FormClosingEventArgs e )
{
    _isRun = false;
}

 

그런데, 스레드의 옵션에 이것 보다 더 좋은 방법이 기본적으로 제공되는데, 바로 IsBackground 속성이다.

스레드에는 Foreground 스레드와 Background 스레드가 존재하며, Foreground 스레드는 메서드의 처리가 끝나고 완료되어야 종료되는 반면에 Background 스레드는 모든 Foreground 스레드가 종료되었을 경우 강제로 중단된다.

프로그램이 시작되는 주 스레드(= 메인 스레드)는 기본값으로 Foreground 스레드이다.

따라서 스레드를 Background 스레드로 만들어서 돌릴 경우 프로그램을 종료했을 때 (주 스레드가 종료되었을 때) Background 스레드는 더이상 Foreground 스레드가 없기 때문에 강제로 중단된다는 것이다.

private void button_Start_Click( object sender, EventArgs e )
{
    new Thread(loop) { IsBackground = true }.Start();
}

private void button_Stop_Click( object sender, EventArgs e )
{
    _isRun = false;
}

private bool _isRun;
private void loop()
{
    _isRun = true;
    
    while ( _isRun )
    {
        // Do something
    }
}

이렇게 코드를 작성하면 메인 스레드가 종료되면 해당 스레드는 무조건 같이 종료된다.