본문 바로가기
Study/C#

C# 10편: 비동기 프로그래밍과 Task

by wawManager 2024. 10. 29.
728x90

1. 비동기 프로그래밍이란?

비동기 프로그래밍은 여러 작업을 동시에 처리하거나 대기 시간 중에 다른 작업을 진행할 수 있도록 하는 프로그래밍 방식입니다. C#에서는 비동기 작업을 처리하기 위해 Task, asyncawait 키워드를 사용합니다. 비동기 작업은 주로 파일 입출력, 네트워크 요청, 데이터베이스 접근과 같은 시간이 오래 걸리는 작업에서 사용됩니다. 이를 통해 프로그램이 응답성을 유지하면서 작업을 효율적으로 처리할 수 있습니다.

2. Task와 비동기 메서드

Task는 비동기 작업의 진행 상태와 결과를 추적할 수 있도록 도와주는 클래스입니다. 비동기 작업이 끝날 때까지 기다리지 않고, 작업이 완료되면 그 결과를 처리할 수 있습니다.

기본 비동기 메서드 구조

비동기 메서드는 Task 또는 Task<T> 타입을 반환하며, async 키워드로 정의됩니다. 메서드 내부에서는 await 키워드를 사용하여 비동기 작업을 수행하고 결과를 기다립니다.

async Task MyAsyncMethod()
{
    // 비동기 작업 수행
    await Task.Delay(1000); // 1초 대기
    Console.WriteLine("비동기 작업 완료");
}

3. async와 await 키워드

  • async: 비동기 메서드를 정의할 때 사용됩니다. 이 키워드는 메서드가 비동기적으로 작업을 수행할 수 있다는 것을 나타냅니다.
  • await: 비동기 작업을 수행할 때 사용됩니다. await는 작업이 완료될 때까지 기다리지만, 해당 작업을 기다리는 동안 다른 작업을 처리할 수 있습니다.

예시: 기본 비동기 메서드

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("비동기 작업 시작");
        await DoWorkAsync(); // 비동기 작업 호출
        Console.WriteLine("비동기 작업 종료");
    }

    static async Task DoWorkAsync()
    {
        Console.WriteLine("작업 진행 중...");
        await Task.Delay(2000); // 2초 대기
        Console.WriteLine("작업 완료");
    }
}

 

출력:

비동기 작업 시작
작업 진행 중...
작업 완료
비동기 작업 종료

4. Task<T>와 반환값 처리

**Task<T>**는 비동기 메서드가 값을 반환하는 경우에 사용됩니다. Task<T>는 작업이 완료된 후 T 타입의 결과를 반환합니다.

예시: Task<T>로 값 반환

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        int result = await CalculateSumAsync(10, 20);
        Console.WriteLine($"계산 결과: {result}");
    }

    static async Task<int> CalculateSumAsync(int a, int b)
    {
        await Task.Delay(1000); // 비동기 작업 처리 중
        return a + b;
    }
}

출력:

계산 결과: 30

5. 여러 비동기 작업 처리

비동기 작업을 여러 개 동시에 실행하고, 그 결과를 처리할 수 있습니다. C#에서는 Task.WhenAll 또는 Task.WhenAny를 사용하여 여러 비동기 작업을 동시에 처리할 수 있습니다.

  • Task.WhenAll: 여러 작업을 병렬로 실행한 후, 모든 작업이 완료될 때까지 기다립니다.
  • Task.WhenAny: 여러 작업 중 하나라도 완료되면 그 작업의 결과를 반환합니다.

예시: Task.WhenAll 사용

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Task<int> task1 = CalculateSumAsync(10, 20);
        Task<int> task2 = CalculateSumAsync(30, 40);

        // 두 작업을 동시에 실행하고, 모두 완료되길 기다림
        int[] results = await Task.WhenAll(task1, task2);

        Console.WriteLine($"결과 1: {results[0]}, 결과 2: {results[1]}");
    }

    static async Task<int> CalculateSumAsync(int a, int b)
    {
        await Task.Delay(1000); // 비동기 작업
        return a + b;
    }
}

출력:

결과 1: 30, 결과 2: 70

6. 비동기 메서드의 예외 처리

비동기 메서드에서도 try-catch 구문을 사용하여 예외를 처리할 수 있습니다. await로 대기 중인 작업에서 예외가 발생하면, 해당 예외는 호출된 곳으로 전달되어 catch 블록에서 처리됩니다.

예시: 비동기 메서드에서 예외 처리

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        try
        {
            await DivideAsync(10, 0); // 0으로 나누기 예외 발생
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine("예외 처리: " + ex.Message);
        }
    }

    static async Task<int> DivideAsync(int a, int b)
    {
        await Task.Delay(500); // 비동기 작업 대기
        return a / b; // 여기서 DivideByZeroException 발생
    }
}

출력:

예외 처리: 0으로 나눌 수 없습니다.

7. 비동기 작업의 취소

C#에서는 CancellationToken을 사용하여 비동기 작업을 취소할 수 있습니다. 작업이 긴 시간이 걸릴 수 있는 경우, 사용자나 시스템에서 작업을 중단할 수 있게 해야 할 때 유용합니다.

예시: 비동기 작업 취소

using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        Task workTask = DoWorkAsync(cts.Token);

        Console.WriteLine("작업을 중단하려면 아무 키나 누르세요.");
        Console.ReadKey();
        cts.Cancel(); // 작업 취소

        try
        {
            await workTask;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("작업이 취소되었습니다.");
        }
    }

    static async Task DoWorkAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            token.ThrowIfCancellationRequested(); // 취소 요청을 처리
            Console.WriteLine($"작업 진행 중... {i + 1}/10");
            await Task.Delay(500);
        }
        Console.WriteLine("작업 완료");
    }
}

출력:

작업 진행 중... 1/10
작업 진행 중... 2/10
...
작업이 취소되었습니다.

8. 비동기 메서드의 반환 타입

비동기 메서드는 주로 Task 또는 **Task<T>**를 반환하지만, 경우에 따라 void를 사용할 수 있습니다. 그러나 void 반환형은 특별한 경우(이벤트 핸들러) 외에는 피해야 합니다. void는 호출한 곳에서 작업의 완료를 추적할 수 없기 때문에 Task 또는 **Task<T>**를 사용하여 작업의 진행 상태를 추적하는 것이 좋습니다.

일반적인 비동기 메서드 반환 타입

  • Task: 반환값이 없고, 비동기 작업의 완료를 나타냅니다.
  • Task<T>: 값을 반환하며, 비동기 작업의 결과를 나타냅니다.
  • void: 특별한 상황에서만 사용(주로 이벤트 핸들러).

9. 실습 예제: 파일 다운로드 시뮬레이션

비동기 작업을 활용하여 파일 다운로드를 시뮬레이션하는 프로그램을 작성해 보겠습니다. 이 프로그램에서는 다운로드 작업이 비동기적으로 진행되고, 동시에 여러 파일을 다운로드할 수 있습니다.

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Task download1 = DownloadFileAsync("file1.zip");
        Task download2 = DownloadFileAsync("file2.zip");
        Task download3 = DownloadFileAsync("file3.zip");

        await Task.WhenAll(download1, download2, download3); // 모든 다운로드가 완료되길 기다림

        Console.WriteLine("모든 파일 다운로드 완료");
    }

    static async Task DownloadFileAsync(string fileName)
    {
        Console.WriteLine($"{fileName} 다운로드 시작");
        await Task.Delay(2000); // 2초간 다운로드 시뮬레이션
        Console.WriteLine($"{fileName} 다운로드 완료");
    }
}

 

출력:

file1.zip 다운로드 시작
file2.zip 다운로드 시작
file3.zip 다운로드 시작
file1.zip 다운로드 완료
file2.zip 다운로드 완료
file3.zip 다운로드 완료
모든 파일 다운로드 완료

10. 요약

  • 비동기 프로그래밍은 작업을 병렬로 처리하여 프로그램의 성능과 응답성을 개선합니다.
  • Taskasync/await를 사용하여 비동기 작업을 쉽게 작성할 수 있으며, 비동기 작업의 결과나 상태를 추적할 수 있습니다.
  • 여러 비동기 작업을 동시에 처리할 수 있으며, Task.WhenAll을 사용하여 모든 작업이 완료될 때까지 기다릴 수 있습니다.
  • 비동기 작업에서 예외 처리와 **취소(CancellationToken)**를 관리할 수 있습니다.

다음 편에서는 C# 디자인 패턴에 대해 학습하여 객체지향 프로그래밍의 효율적인 패턴을 이해하겠습니다.

 

728x90