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

nomad-programmer

[Programming/C#] 스레드의 일생과 상태 변화 본문

Programming/C#

[Programming/C#] 스레드의 일생과 상태 변화

scii 2020. 9. 29. 00:42

.NET 프레임워크는 스레드의 상태를 ThreadState 열거형에 정의해두었다. 다음 표와 같다.

상태 설명
Unstarted 스레드 객체를 생성한 후 Thread.Start() 메소드가 호출되기 전의 상태이다.
Running 스레드가 시작하여 동작 중인 상태를 나타낸다. Unstarted 상태의 스레드를 Thread.Start() 메소드를 통해 이 상태로 만들 수 있다.
Suspended 스레드의 일시 중단 상태를 나타낸다. 스레드를 Thread.Suspend() 메소드를 통해 이 상태로 만들 수 있으며, Suspended 상태인 스레드는 Thread.Resume() 메소드를 통해 다시 Running 상태로 만들 수 있다.
WaitSleepJoin 스레드가 블록(Block)된 상태를 나타낸다. 메소드 이름이 WaitSleepJoin인 이유는 스레드에 대해 Monitor.Enter(), Thread.Sleep() 또는 Thread.Join() 메소드를 호출하면 이 상태가 되기 때문이다.
Aborted 스레드가 취소된 상태를 나타낸다. Thread.Abort() 메소드를 호출하면 이 상태가 된다. Aborted 상태가 된 스레드는 다시 Stopped 상태로 전환되어 완전히 중지 된다.
Stopped 중지된 스레드의 상태를 나타낸다. Abort() 메소드를 호출하거나 스레드가 실행 중인 메소드가 종료되면 이 상태가 된다.
Background 스레드가 백그라운드로 동작하고 있음을 나타낸다. 포어그라운드(Foreground) 스레드는 하나라도 살아있는 한 프로세스가 죽지 않지만, 백그라운드는 하나가 아니라 열 개가 살아 있어도 프로세스가 죽고 사는 것에는 영향을 미치지 않는다. 하지만 프로세스가 죽으면 백그라운드 스레드들도 모두 죽는다. Thread.IsBackground 속성에 true 값을 입력함으로써 스레드를 이 상태로 바꿀 수 있다.

스레드의 상태 변화에는 규칙이 있다. 예를 들어 Aborted 상태의 스레드는 절대 Running 상태로 천이되지 못하고, Running 상태의 스레드는 Unstarted 상태로 바뀔 수 없다. 

위 그림에 Background로의 천이하는 과정이 표현되어 있지 않은데, 그 이유는 Background 상태는 그저 스레드가 어떻게 동작하고 있는지에 관한 정보를 나타낼 뿐이기 때문이다.

ThreadState는 Flags 어트리뷰트를 갖고 있다. Flags는 자신이 수식하는 열거형을 비트 필드(Bit Field), 즉 플래그 집합으로 처리할 수 있음을 나타낸다.

비트 필드(bit field)란?

비트 필드는 원래 C언어 등에서 구조체를 선언할 때 바이트 단위가 아닌 비트 단위로 선언한 필드를 말하며 주로 비트 단위의 플래그를 표현하기 위해 사용한다.
C언어가 1970년대 초에 만들어졌고 그 당신의 메모리 가격과 용량을 생각하면 비트 필드가 존재해야 하는 이유가 충분해 보인다. 한 바이트로 0~255까지 표현할 수 있는데 기껏해야 0, 1, 2, 3, ... 7 정도의 값을 갖는 플래그를 표현하려고 한 바이트를 모두 사용하면 메모리 낭비다.
C# 언어에서도 비트 필드를 사용할 수 있도록 Flags 어트리뷰트를 .NET 프레임워크에 선언해두었다.

2020/06/16 - [Programming/C] - [C] 비트 단위 분리

 

[C] 비트 단위 분리

구조체 문법으로 비트 단위를 분리할 수 있다. #pragma warning(disable: 4996) #include // 비트 단위 정보를 다룰 수 있도록 구조체를 선언 (총 1바이트) // 구조체 멤버 하나하나가 1비트 struct BitType { uns..

nomad-programmer.tistory.com

Flags 어트리뷰트를 사용하지 않는 다음과 같은 평범한 열거형은 열거 요소에 대응하는 값들만 표현한다.

// 0, 1, 2, 3
enum MyEnum
{
    houdini,
    maya,
    max,
    blender
};

Console.WriteLine((MyEnum)0);  // houdini
Console.WriteLine((MyEnum)1);  // maya
Console.WriteLine((MyEnum)2);  // max
Console.WriteLine((MyEnum)3);  // blender

// 열거 요소에 대응하지 못하는 값은 형변환을 시도하여 원래 값으로 표현된다.
Console.WriteLine((MyEnum)4);  // 4
Console.WriteLine((MyEnum)5);  // 5

반면에 다음과 같이 Flags 어트리뷰트를 갖는 열거형은 요소들의 집합으로 구성되는 값들도 표현할 수 있다.

[Flags]
enum MyEnum
{
    houdini,
    maya,
    max,
    blender
};

Console.WriteLine((MyEnum)0);  // houdini
Console.WriteLine((MyEnum)1);  // maya
Console.WriteLine((MyEnum)2);  // max
Console.WriteLine((MyEnum)3);  // blender

// Flag 어트리뷰트는 열거형의 요소들의 집합으로 구성되는 값들도 표현할 수 있다.
Console.WriteLine((MyEnum)4);  // maya, blender
Console.WriteLine((MyEnum)5);  // max, blender

스레드는 동시에 두 가지 이상의 상태를 가질 수 있다. 가령 Suspended 상태이면서도 WaitSleepJoin 상태를 가질 수도 있고, Background 상태이면서 Stopped 상태일 수도 있다. 그래서 ThreadState는 두 가지 이상의 상태를 동시에 표현하기 위해 이 Flags 어트리뷰트가 수식되어 있는 것이다.

ThreadState의 각 요소들 값 확인하는 예제

using System;
using System.Threading;

namespace CSharpExample
{
    internal class MainApp
    {
        private static void PrintThreadState(ThreadState state)
        {
            Console.WriteLine("{0,-16}: {1}", state, (int)state);
        }

        static int Main(string[] args)
        {
            PrintThreadState(ThreadState.Running);
            PrintThreadState(ThreadState.StopRequested);
            PrintThreadState(ThreadState.SuspendRequested);
            PrintThreadState(ThreadState.Background);
            PrintThreadState(ThreadState.Unstarted);
            PrintThreadState(ThreadState.Stopped);
            PrintThreadState(ThreadState.WaitSleepJoin);
            PrintThreadState(ThreadState.Suspended);
            PrintThreadState(ThreadState.AbortRequested);
            PrintThreadState(ThreadState.Aborted);
            PrintThreadState(ThreadState.Aborted | ThreadState.Stopped);

            return 0;
        }
    }
}


/* 결과

Running         : 0
StopRequested   : 1
SuspendRequested: 2
Background      : 4
Unstarted       : 8
Stopped         : 16
WaitSleepJoin   : 32
Suspended       : 64
AbortRequested  : 128
Aborted         : 256
Stopped, Aborted: 272

*/
상태 십진수 이진수
Running 0 000000000
StopRequested 1 000000001
SuspendRequested 2 000000010
Background 4 000000100
Unstarted 8 000001000
Stopped 16 000010000
WaitSleepJoin 32 000100000
Suspended 64 001000000
Aborted 256 100000000

Thread 객체의 ThreadState 필드를 통해 상태를 확인할 때는 반드시 비트 연산을 이용해야 한다. ThreadState 열거형이 여러 상태를 동시에 나타낼 수 있도록 만들어져 있기 때문이다.

// Thread 객체의 ThreadState 필드의 값을 확인하는 예제

if (t1.ThreadState & ThreadState.Aborted == ThreadState.Aborted)
    Console.WriteLine("스레드 정지됨");
else if (t1.ThreadState & ThreadState.Stopped == ThreadState.Stopped)
    Console.WriteLine("스레드 취소됨");
Comments