본문 바로가기
C#/쓰레드

C# TPL: 태스크 생성 방식과 실행 흐름 완벽 이해

by 공부봇 2025. 1. 17.
반응형

C# 태스크 병렬 라이브러리(TPL) - 태스크 생성

**태스크 병렬 라이브러리(TPL)**은 멀티스레드 프로그래밍을 쉽게 하기 위해 .NET에서 제공하는 기능입니다. Task는 TPL의 핵심 클래스 중 하나로, 비동기 작업을 처리하고 스레드 풀(Thread Pool)을 활용하여 작업을 병렬로 실행합니다.


예제 코드 분석

internal class Program
{
    static void TaskMethod(string name)
    {
        Console.WriteLine("Task {0} is running on thread id {1}. Is thread pool thread : {2}",
            name,
            Thread.CurrentThread.ManagedThreadId,
            Thread.CurrentThread.IsThreadPoolThread);
    }

    static void Main(string[] args)
    {
        var t1 = new Task(() => TaskMethod("Task 1"));
        var t2 = new Task(() => TaskMethod("Task 2"));

        t2.Start(); // Task 2 시작
        t1.Start(); // Task 1 시작
        
        Task.Run(() => TaskMethod("Task 3")); // Task 3 시작
        Task.Factory.StartNew(() => TaskMethod("Task 4")); // Task 4 시작
        Task.Factory.StartNew(() => TaskMethod("Task 5"), TaskCreationOptions.LongRunning); // Task 5 시작 (LongRunning 옵션)

        Thread.Sleep(TimeSpan.FromSeconds(1)); // 메인 스레드 대기 (태스크 실행 기다림)
    }
}

코드 동작 과정

  1. Task 생성 
    • 태스크 생성:
      • t1과 t2는 Task 객체를 생성했지만, 아직 실행되지 않았습니다.
    • 중요: 태스크를 생성한다고 해서 바로 실행되지 않습니다. .Start()를 호출해야 태스크가 실행됩니다.
    • var t1 = new Task(() => TaskMethod("Task 1"));
      var t2 = new Task(() => TaskMethod("Task 2"));
  2. 태스크 실행
    • t2.Start()와 t1.Start()를 호출하면 태스크가 실행되고, 스레드 풀(Thread Pool)에서 스레드를 가져와 작업을 수행합니다.
    • 태스크 실행 순서는 t2가 먼저 호출되었지만, 실제 실행 순서는 스레드 스케줄러에 의해 결정되므로 순서가 보장되지 않습니다.
    • t2.Start();
      t1.Start();
  3. Task.Run
    • 비동기 태스크 실행:
      • Task.Run은 태스크를 생성하고 즉시 실행합니다. 내부적으로 Task.Factory.StartNew를 호출하여 스레드 풀에서 실행합니다.
    • 가장 간단한 태스크 실행 방식입니다.
    • Task.Run(() => TaskMethod("Task 3"));
  4. Task.Factory.StartNew
    • Task.Factory.StartNew는 태스크를 생성하고 바로 실행합니다.
    • 두 번째 태스크에서는 TaskCreationOptions.LongRunning을 사용했는데, 이는 태스크가 스레드 풀이 아닌 전용 스레드에서 실행되도록 지정합니다.
    • 용도: 태스크가 오래 실행되거나 CPU를 많이 사용하는 작업일 경우, 다른 스레드 풀 작업에 영향을 주지 않기 위해 사용합니다.
  5. Thread.Sleep
    • 메인 스레드를 1초 동안 중단하여 모든 태스크가 실행을 완료할 시간을 줍니다.
    • Thread.Sleep(TimeSpan.FromSeconds(1));

빌드 결과 예상

출력은 태스크 실행 순서가 보장되지 않기 때문에 실행마다 다를 수 있습니다. 대략적인 출력 예시는 아래와 같습니다:

Task 2 is running on thread id 3. Is thread pool thread : True
Task 1 is running on thread id 4. Is thread pool thread : True
Task 3 is running on thread id 5. Is thread pool thread : True
Task 4 is running on thread id 6. Is thread pool thread : True
Task 5 is running on thread id 7. Is thread pool thread : False

태스크 생성과 관련된 핵심 개념

  1. Task 생성
    • Task는 작업 단위를 정의하는 객체입니다.
    • 태스크는 생성 후 실행(Start())을 명시적으로 호출해야 실행됩니다.
  2. Task.Run
    • 태스크를 생성하고 즉시 실행합니다.
    • 가장 간단하고 일반적인 태스크 실행 방식입니다.
  3. Task.Factory.StartNew
    • Task.Run과 유사하지만 더 세부적인 옵션을 제공합니다.
    • 옵션:
      • TaskCreationOptions.LongRunning: 전용 스레드에서 실행.
      • TaskCreationOptions.PreferFairness: 태스크가 가능한 한 공정하게 실행되도록 요청.
  4. 스레드 풀(Thread Pool)
    • 대부분의 태스크는 스레드 풀에서 실행됩니다.
    • 스레드 풀은 작업을 효율적으로 관리하며, 가벼운 태스크에 적합합니다.
  5. 전용 스레드
    • TaskCreationOptions.LongRunning은 스레드 풀 대신 전용 스레드를 생성합니다.
    • 리소스를 많이 사용하거나 오래 실행되는 작업에 적합합니다.

Task.Run vs Task.Factory.StartNew

특징 Task.Run Task.Factory.StartNew

코드 간결성 간결함 세부 옵션 제공 가능
비동기 작업 주로 사용됨 비동기 및 세부 조정 가능
옵션 지원 옵션 지원하지 않음 LongRunning, PreferFairness 등
사용 추천 일반적인 태스크 실행에 적합 세부 동작 제어가 필요한 경우

어떤 태스크 생성 방식을 써야 할까?

  1. 간단한 태스크:
    • Task.Run을 사용하세요.
    • 예: 비동기 작업을 빠르게 시작할 때.
  2. 세부 설정 필요:
    • Task.Factory.StartNew를 사용하세요.
    • 예: 오래 실행되는 태스크(LongRunning)나 공정성을 강조하는 작업(PreferFairness).
  3. 성능 고려:
    • 스레드 풀이 효율적이라면 Task.Run 또는 기본 Task.Factory.StartNew를 사용합니다.
    • 전용 스레드가 필요하면 TaskCreationOptions.LongRunning을 사용합니다.

결론

  • 이 코드에서는 다양한 태스크 생성 방식을 보여주고 있습니다.
  • 일반적인 작업은 Task.Run을 사용하고, 세부 제어가 필요한 경우 Task.Factory.StartNew를 활용합니다.
  • TaskCreationOptions.LongRunning은 스레드 풀이 아닌 전용 스레드에서 태스크를 실행하기 위해 사용됩니다.

추가적으로 궁금한 점이 있다면 언제든 물어보세요! 😊

반응형