호우동의 개발일지

Today :

article thumbnail

1. Thread


1.1. Thread 생성

<code />
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과 별도로 실행되는 것

 


1.2. foreground vs background

<code />
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 출력 기점으로 얼마간 출력되다가 더이상 안됨

 


1.3. Join

  • Thread를 호출한 함수가 Thread의 종료시까지 기다리는 것
<code />
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을 했을 때

 


2. ThreadPool

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

 


2.1. 주요 메소드 및 프로퍼티

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

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

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

 


2.2. ThreadPool 사용 시 주의할 점

<code />
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를 사용

 


2.3. Task

<code />
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 옵션을 생성해 둔 것은 별도의 스레드를 생성한다.