Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
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
Archives
Today
Total
관리 메뉴

nomad-programmer

[Programming/C#] 스레드 인터럽트 (Thread Interrupt) 본문

Programming/C#

[Programming/C#] 스레드 인터럽트 (Thread Interrupt)

scii 2020. 9. 29. 01:49

스레드는 수명이 다해 스스로 종료되는 것이 가장 좋지만, 불가피하게 스레드를 강제로 종료시켜야 하는 경우가 있다.

Abort() 메소드를 사용할 때는 도중에 강제로 중단된다 해도 프로세스 자신이나 시스템에 영향을 받지 않는 작업에 한 해 사용하는 것이 좋다. 

스레드가 수행 중인 작업이 강제로 중단되는 경우 시스템에 악영향을 미칠 수 있다면 조금 더 부드러운 방법을 택해야 한다. 

Thread.Interrupt() 메소드는 스레드가 한참 동작 중인 상태(Running 상태)를 피해서 WaitJoinSleep 상태에 들어갔을 때 ThreadInterruptedException 예외를 던져 스레드를 중지 시킨다. Abort()와 비슷하지만 Thread.Interrupt() 메소드가 조금 더 나은 방법이라 할 수 있다.

Thread.Interrupt() 메소드는 스레드가 이미 WaitSleepJoin 상태에 있을 때는 즉시 중단시키지만, 이 상태가 아닌 경우에는 스레드를 지켜보고 있다가 나중에 WaitSleepJoin 상태가 되면 그제서야 스레드를 중단시킨다. 이런 특징 때문에 프로그래머는 최소한 코드가 "절대로 중단되면 안 되는" 작업을 하고 있을 때에는 중단되지 않는다는 보장을 받을 수 있다.

  • 스레드가 WaitSleepJoin 상태일 때, Interrupt()를 호출하면 즉시 ThreadInterruptedException 발생
  • 스레드가 Running 상태일 때, Interrupt()를 호출해 두면 "나중에" WaitSleepJoin 상태가 됐을 때 ThreadInterruptedException 발생
static void DoSomething()
{
    try
    {
        for(int i=0; i<1000; i++)
        {
            Console.WriteLine("DoSomething: {0}", i);
            Thread.Sleep(10);
        }
    }
    catch (ThreadInterruptedExcetion err)
    {
        // ...
    }
    finally
    {
        // ...
    }
}

static void Main(string[] args)
{
    Thread t1 = new Thread(new ThreadStart(DoSomething));
    
    t1.Start();
    
    // 스레드 취소(종료)
    t1.Interrupt();
    
    t1.Join();
}

AbortingThread와 대부분 비슷하다. 차이점이라면 Abort() 메소드 대신 Interrupt() 메소드를 호출한다는 것과 ThreadStart 대리자가 참조하는 SideTask.KeepAlive() 메소드의 시작 부분에서 SpinWait() 메소드를 호출함으로써 Interrupt() 메소드가 호출될 때 스레드의 상태가 한동안 Running 상태를 갖도록 하게 했다는 것 정도이다.

using System;
using System.Threading;

namespace CSharpExample
{
    class SideTask
    {
        private int count;

        public SideTask(int count) => this.count = count;

        public void KeepAlive()
        {
            try
            {
                Console.WriteLine("Running thread isn't gonna be interrupted");

                // 메소드는 Sleep()과 유사하게 스레드를 대기하도록 하지만 Sleep()과는 달리 
                //스레드가 Running 상태를 유지하게 한다.
                Thread.SpinWait(1000000000);

                while (count > 0)
                {
                    Console.WriteLine($"{ count--} left");
                    Console.WriteLine("Entering into WaitJoinSleep State...");
                    Thread.Sleep(10);
                }
                Console.WriteLine("Count: 0");
            }
            catch (ThreadInterruptedException err)
            {
                Console.WriteLine(err);
            }
            finally
            {
                Console.WriteLine("Clearing resource...");
            }
        }
    }

    internal class MainApp
    {
        static int Main(string[] args)
        {
            SideTask task = new SideTask(100);
            Thread t1 = new Thread(new ThreadStart(task.KeepAlive));
            t1.IsBackground = false;

            Console.WriteLine("Starting thread...");
            t1.Start();

            Thread.Sleep(100);

            Console.WriteLine("Interrupting thread...");
            t1.Interrupt();

            Console.WriteLine("Waiting until thread stops...");
            t1.Join();

            Console.WriteLine("Finished");

            return 0;
        }
    }
}


/* 결과

Starting thread...
Running thread isn't gonna be interrupted
Interrupting thread...
Waiting until thread stops...
100 left
Entering into WaitJoinSleep State...
System.Threading.ThreadInterruptedException: 스레드가 대기 상태에서 인터럽트되었습니다.
   위치: System.Threading.Thread.SleepInternal(Int32 millisecondsTimeout)
   위치: System.Threading.Thread.Sleep(Int32 millisecondsTimeout)
   위치: CSharpExample.SideTask.KeepAlive() 파일 D:\workspace\c#\study\this_is_csharp\HelloWorld\HelloWorld.cs:줄 26
Clearing resource...
Finished

*/

SpinWait() 메소드는 별로 쓸 일이 없다. 이 메소드는 Sleep()과 유사하게 스레드를 대기하게 하지만 Sleep()과는 달리 스레드가 Running 상태를 유지하게 한다.
이 메소드를 사용한 것은 Interrupt() 메소드를 호출하는 시점에 스레드가 Running 상태를 유지하도록 하기 위함이다. Interrup() 메소드가 호출된 스레드는 Thread.Sleep(10)에 의해 WaitSleepJoin 상태에 들어가고, 이 때 인터럽트가 발생한다.

Comments