Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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
05-17 00:00
관리 메뉴

nomad-programmer

[Programming/C#] 스레드 임의 종료 (Abort) 본문

Programming/C#

[Programming/C#] 스레드 임의 종료 (Abort)

scii 2020. 9. 28. 23:41

프로세스는 사용자가 작업 관리자 등을 이용하여 임의로 죽일 수 있다. 하지만 프로세스 안에서 동작하는 각 스레드는 그런 식으로 죽일 수 없다. 살아 있는 스레드를 죽이려면 다음 예제와 같이 그 스레드를 나타내는 Thread 객체의 Abort() 메소드를 호출해줘야 한다.

static void DoSomething()
{
    try
    {
        for(int i=0; i<1000; i++)
        {
            Console.WriteLIne("DoSomething: {0}", i);
            Thread.Sleep(10);
        }
    }
    catch (ThreadAbortedException)
    {
        // ...
    }
    finally
    {
        // ...
    }
}

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

Abort() 메소드를 사용할 때는 고려해야 할 사항이 있다. 그것은 바로 Abort() 메소드를 호출한다고 해서 동작하던 스레드가 즉시 종료된다는 보장이 없다는 사실이다. Thread 객체의 Abort() 메소드를 호출하면 CLR은 해당 스레드가 실행 중이던 코드에 ThreadAbortException을 호출한다. 이 때 예외를 catch하는 코드가 있으면 이 예외를 처리한 다음, fianlly 블록까지 실행한 후에야 해당 스레드는 완전히 종료된다.
그래서 Abort() 메소드를 호출할 때는 이 처리 시간을 반드시 염두에 둬야 하는 것이다.

Thread.Abort() 메소드는 사용하지 않는 것이 좋다.

한 스레드가 동기화를 위해 어떤 자원을 자신이 독점하고자 잠근 후 그 자원을 해제하지 못한 채 Abort() 메소드를 호출 당해 갑자기 죽어버리면, 그 자원에 접근하고자 하는 다른 스레드들은 그대로 꼼작도 못하는 신세가 되기 때문이다.

그래서 프로그래머들 사이에서는 Abort() 메소드를 사용하는 것이 goto 문을 사용하는 것만큼이나 금기시되어 있다. ThreadStart 대리자가 (무한) 반복문을 포함하는 코드를 참조하고 있다면 Abort() 메소드를 호출하는 대신 그 반복문을 매회 반복할 때마다 계속 수행할 것인지를 확인하는 변수를 다른 스레드들과 공유하고, 그 변수의 값을 외부 스레드에서 변경하도록 하는 것이 낫다.

Abort 관련 예제

using System;
using System.Threading;

namespace CSharpExample
{
    class SideTask
    {
        int count;

        public SideTask(int count)
        {
            this.count = count;
        }

        public void KeepAlive()
        {
            try
            {
                while (count > 0)
                {
                    Console.WriteLine($"{count--} left");
                    Thread.Sleep(10);
                }
                Console.WriteLine("Count: 0");
            }
            catch (ThreadAbortException err)
            {
                Console.WriteLine(err);
                Thread.ResetAbort();
            }
            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("Aborting thread...");
            t1.Abort();

            Console.WriteLine("Wating until thread stop...");
            t1.Join();

            Console.WriteLine("Finished");

            return 0;
        }
    }
}


/* 결과

Starting thread...
100 left
99 left
98 left
97 left
96 left
95 left
94 left
93 left
Aborting thread...
System.Threading.ThreadAbortException: 스레드가 중단되었습니다.
   위치: 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:줄 22
Wating until thread stop...
Clearing resource...
Finished

*/
Comments