서버/서버 이론

쓰레드 (Thread)

잼잼재미 2025. 3. 3. 13:28

쓰레드 생성


using System;
using System.Threading;

namespace ServerCore
{ 
    internal class Program
    {
        static void MainThread()
        {
            Console.WriteLine("Hello, Thread!");
        }

        static void Main(string[] args)
        {
            Thread t = new Thread(MainThread);	// 쓰레드 생성
            t.Name = "Test Thread";	// 쓰레드 이름 설정
            t.Start();	// 쓰레드 시작
            
            Console.WriteLine("Hello, World!");
        }
    }
}

 

 

※ 실행 순서

 

코드의 순서로 보면 쓰레드가 먼저 실행되고, 메인 함수가 실행되는 것이 맞지만, 쓰레드의 실행 순서는 예측할 수 없기 때문에 코드가 실행되는 순서는 알 수 없다. 각각 쓰레드는 각자 따로 실행된다고 생각 할 것!

 

 

Background 


using System;
using System.Threading;

namespace ServerCore
{ 
    internal class Program
    {
        static void MainThread()
        {
            Console.WriteLine("Hello, Thread!");
        }

        static void Main(string[] args)
        {
            Thread t = new Thread(MainThread);
            t.IsBackground = false;		// default
            t.IsBackground = true;		// Background 설정
            t.Start();
            
            Console.WriteLine("Hello, World!");
        }
    }
}

 

IsBackground를 true로 설정하면 Main 함수가 종료되면 해당 쓰레드도 함께 종료 됨

 

 

Join


using System;
using System.Threading;

namespace ServerCore
{ 
    internal class Program
    {
        static void MainThread()
        {
            while (true)
                Console.WriteLine("Hello, Thread!");
        }

        static void Main(string[] args)
        {
            Thread t = new Thread(MainThread);
            t.IsBackground = true;
            t.Start();
            Console.WriteLine("Hello, Waiting for Thread!");
            t.Join();
            Console.WriteLine("Hello, World!");
        }
    }
}

 

해당 쓰레드가 종료될 때 까지 대기

 

 

ThreadPool


  • 쓰레드를 생성하고 실행하는 것은 큰 부담이 됨 
  • C#에서는 Thread를 직접 생성하는 경우는 거의 없고, ThreadPool을 이용
  • 간단한 작업을 수행하고 종료되어야 하는 쓰레드는 ThreadPool을 사용
  • IsBackground가 true 인 상태로 생성 (메인 함수가 종료되면 함께 종료)
  • 쓰레드 최대값 만큼 무한루프가 실행되면 더 이상 ThreadPool을 실행 할 수 없음
  • ex) 정직원이 아닌 단기 알바

 

using System;
using System.Threading;

namespace ServerCore
{ 
    internal class Program
    {
        static void MainThread(object state)
        {
            for (int i = 0; i < 10; i++)
            Console.WriteLine("Hello, Thread!");
        }

        static void Main(string[] args)
        {
            ThreadPool.SetMinThreads(1, 1);	// 쓰레드 최소값
            ThreadPool.SetMaxThreads(5, 5);	// 쓰레드 최대값
            ThreadPool.QueueUserWorkItem(MainThread);
        }
    }
}

 

 

Task


  • 구현하고 싶은 작업 자체를 의미
  • Thread는 고수준의 스레드 제어가 필요할 때 사용, Task는 비동기 작업을 간결하게 표현하고 싶을 때 사용
  • 내부적으로 ThreadPool을 이용
  • 아주 오래 걸리는 (무한 루프 같은) 작업을 할 때 유용
  • ThreadPool의 사용 가능한 갯수에 count 되지 않도록 설정 가능

 

 

using System.Threading.Tasks;

Task t = new Task(() => { while (true) { } }, TaskCreationOptions.LongRunning);	// count X
t.Start();

Task t = new Task(() => { while (true) { } });	// count O
t.Start();

 

 

사용법


static bool _stop = false;

static void MainThread()
{
    Console.WriteLine("쓰레드 시작!");

    while (_stop == false)
    {

    }

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

static void Main(string[] args)
{
    Task t = new Task(MainThread);
    t.Start();  // 쓰레드 실행

    Thread.Sleep(1000); // 쓰레드가 실행될 수 있도록 1초동안 대기

    _stop = true;

    Console.WriteLine("Stop 호출");
    Console.WriteLine("종료 대기중");

    t.Wait();   // 쓰레드가 종료될때까지 대기

    Console.WriteLine("종료 성공");
}

 

 

 

쓰레드가 시작 후, 1초를 대기하고 쓰레드를 종료시킨다. 이렇게 쓰레드를 통해 코드를 병렬적으로 실행 가능하다.

 

 

※ 컴파일러 최적화

Debug 모드에서는 위와 같이 코드가 병렬적으로 잘 실행되는 것을 볼 수 있다. 하지만 실제 라이브 게임에서 사용되는 Release 모드에서는 쓰레드의 무한루프에서 벗어날 수 없는 것을 확인할 수 있다. 그 이유는 Release 모드에서는 코드 최적화가 일어나는데 위의 쓰레드에서 while문을 벗어나는 코드가 없기 때문에 멋대로 코드를 최적화를 해 준다. 이것은 volatile을 static bool 앞에 붙여서 휘발성 데이터로 만들어서 코드 최적화를 막는 방법이 있다. 하지만 volatile는 복잡하고 이상한 기능이 많기 때문에 전문가들은 사용을 권장하지 않는다. 그래서 대충 내용만 알고 있을 것!

'서버 > 서버 이론' 카테고리의 다른 글

Lock  (0) 2025.03.27
InterLocked  (0) 2025.03.24
메모리 배리어  (0) 2025.03.19
캐시 이론  (0) 2025.03.18
프로세스와 쓰레드  (0) 2025.03.02