호우동의 개발일지

Today :

article thumbnail

Thread


Thread 생성

using System;
using System.Threading; // 추가해줘야 사용 가능
namespace ServerCore
{
    public class Program
    {
        // 사용할 함수(쓰레드)
        static void MainThread()
        {
            Console.WriteLine("Create Thread");
        }
        static void Main(string[] args)
        {
            //Thread 이름 = new Thread(함수 이름);
            Thread thread = new Thread(MainThread);
            thread.Start();
        }
    }
}

출력 결과
출력 결과

  • using System.Threading을 추가해 줘야 사용이 가능하다.
  • thread.Start()를 해야 만들어둔 thread가 실행된다.
  • thread는 Main과 별도로 실행되는 것

 


foreground vs background

using System;
using System.Threading;
namespace ServerCore
{
    public class Program
    {
        static void MainThread()
        {
            //MainThread에 출력 메시지를 무한루프 돌린다고 가정
            while(true) Console.WriteLine("Create Thread");
        }
        static void Main(string[] args)
        {
            Thread thread = new Thread(MainThread);

            // false = foreground
            // true = background
            thread.IsBackground = true;
            thread.Start();
            Console.WriteLine("ThisIsMain");
        }
    }
}
  • 기본적으로 thread를 생성하면 foreground로 생성됨
  • IsBackgrond 프로퍼티로 설정 가능
    • false = foreground
    • true = background
  • foreground : Main이 종료돼도 스레드가 종료되지 않고 살아있음
    • Main이 종료돼도 끝나지 않고 계속 생성됨
      출력
      끝나지 않고 Create Thread가 계속 출력된다.
  • background : Main이 종료되면 스레드도 같이 종료됨
    • ThisIsMain이 호출되고 얼마 되지 않아 CreateThread도 종료됨
      Main Thread 출력 기점으로 얼마간 출력되다가 더이상 안됨

 


Join

  • Thread를 호출한 함수가 Thread의 종료시까지 기다리는 것
public class Program
    {
        static void MainThread()
        {
            for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
        }
        static void Main(string[] args)
        {
            Thread thread = new Thread(MainThread);

            // false = foreground
            // true = background
            thread.IsBackground = true;
            thread.Start();
            Console.WriteLine("Main Is Waiting");
            thread.Join(); // MainThrea의 종료까지 기다
            Console.WriteLine("System End");
        }
    }
}

좌 : Join을 하지 않았을 때 // 우 : Join을 했을 때
좌 : Join을 하지 않았을 때 // 우 : Join을 했을 때

 


ThreadPool

  • Thread를 직접 만들어서 관리하는 것은 시스템적으로 부담이 큰 일
  • C#에서 이미 만들어져 있는 Thread를 미리 빌려와서 사용 후 다시 반환
    → 쓰레드를 만드는 것보다 부담이 훨씬 적음

 


주요 메소드 및 프로퍼티

  • ThreadPool.QueueUserWorkItem(함수이름)
    • 쓰레딩하고자 하는 함수를 동작시킴 (Thread의 start()와 비슷)
    • 이때 해당 함수는 매개변수로 object 혹은 object? 클래스형을 가져야 함

  • ThreadPool.SetMinThreads(스레드 개수, 비동기 I/O Thread 개수)
    • 최소로 빌려올 수 있는 스레드 개수

  • ThreadPool.SetMaxThreads(스레드 개수, 비동기 I/O Thread 개수)
    • 최대로 빌려올 수 있는 스레드 개수

 


ThreadPool 사용 시 주의할 점

public class Program
    {
        static void MainThread(object? obj)
        {
            for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
        }
        static void Main(string[] args)
        {
            // 쓰레드를 최소 1개 최대 3개까지밖에 못빌려주도록 제한
            ThreadPool.SetMinThreads(1, 1);
            ThreadPool.SetMaxThreads(3, 3);

            //3번 반복
            for(int i = 0; i < 3; i++)
                // 람다식으로 자원을 한번 잡으면 영원히 반환하지 않는 함수를 만듦
                ThreadPool.QueueUserWorkItem((obj) => { while (true) ; });
            // 쓰레드 풀 사용
            ThreadPool.QueueUserWorkItem(MainThread);
            while (true) ; 
        }
    }

아무것도 안뜸
아무것도 안뜸

  • Thread Pool은 자원 공유 → 대기 → 반환 → 공유 → 대기 → 반환을 반복한다.
  • Thread Pool에 남아있는 자원이 없다면 더 이상 스레드를 생성할 수 없다.
  • 위와 같은 경우에선 이미 3개의 스레드가 실행되고 있기 때문에 4번째 스레드를 생성하지 못한다.
  • Thread Pool 에는 짧은 함수만을 넣는 것이 좋음
  • 이를 해결하는 방법으로 Task를 사용

 


Task

public class Program
    {
        static void MainThread(object? obj)
        {
            for(int i = 0; i < 5; i++) Console.WriteLine("Create Thread");
        }
        static void Main(string[] args)
        {
            // 쓰레드를 최소 1개 최대 3개까지밖에 못빌려주도록 제한
            ThreadPool.SetMinThreads(1, 1);
            ThreadPool.SetMaxThreads(3, 3);


            for (int i = 0; i < 3; i++)
            {
                // Task의 LongRunning 옵션 사용
                Task t = new Task(() => { while (true) ; }, TaskCreationOptions.LongRunning);
                t.Start();
            }

            ThreadPool.QueueUserWorkItem(MainThread);
            while (true) ; 
        }
    }

출력 결과가 제대로 나옴

  • Task를 생성하면서 TaskCreationOptions.LongRunning 옵션을 사용한다.
  • Task도 기본적으로 ThreadPool에서 돌아가지만
    LongRunning 옵션을 생성해 둔 것은 별도의 스레드를 생성한다.