BeginInvoke와 EndInvoke를 활용한 비동기 메서드 호출

비동기 처리에서 BeginInvoke는 스레드 풀에서 하나의 스레드를 할당하고, 지정된 메서드를 별도의 스레드에서 실행하도록 합니다. 이 과정에서 BeginInvoke는 호출자 스레드에 IAsyncResult 인터페이스를 통해 상태 정보를 반환합니다. 이 인터페이스는 비동기 작업의 진행 상황을 추적하는 데 사용됩니다.

EndInvoke는 비동기 호출의 결과 값을 가져오며, 해당 작업이 완료된 후 사용된 리소스를 정리합니다. 주요 특징은 다음과 같습니다:

  • BeginInvoke가 반환한 IAsyncResult 객체를 입력으로 받아, 관련된 비동기 작업 스레드를 식별합니다.
  • 작업 스레드가 종료된 경우, 그 상태를 정리하고 자원을 해제합니다.
  • 메서드의 반환 값을 추출하여 EndInvoke의 결과로 제공합니다.
  • 작업이 아직 완료되지 않은 경우, 호출 스레드는 대기 상태에 들어가 작업이 끝날 때까지 기다립니다.

따라서 모든 BeginInvoke 호출은 반드시 EndInvoke로 마무리되어야 하며, 그렇지 않으면 리소스 누수가 발생할 수 있습니다. 또한 EndInvoke에서 예외가 발생하면 해당 예외가 전파됩니다.

비동기 프로그래밍 패턴 세 가지

1. 대기 모드 (일시 정지)

비동기 호출 이후 EndInvoke를 호출해 결과를 기다리는 방식입니다. 이 경우 메인 스레드는 결과를 받을 때까지 차단됩니다.

delegate long MyAdd(int a, int b);

class Program
{
    static void Main(string[] args)
    {
        MyAdd add = new MyAdd(ComputeSum);
        Console.WriteLine("시작 전");

        IAsyncResult result = add.BeginInvoke(10, 20, null, null);
        Console.WriteLine("비동기 호출 완료");

        // 다른 작업 수행
        Console.WriteLine("작업 중...");

        // 결과 기다림
        long finalResult = add.EndInvoke(result);
        Console.WriteLine($"결과: {finalResult}");

        Console.ReadLine();
    }

    static long ComputeSum(int x, int y)
    {
        Console.WriteLine("계산 중...");
        Thread.Sleep(5000);
        return x + y;
    }
}

2. 반복 확인 모드 (스핀 대기)

IAsyncResult.IsCompleted 속성을 주기적으로 검사하며, 완료되지 않았다면 다른 작업을 수행합니다. 완료되면 결과를 처리합니다.

static void Main(string[] args)
{
    MyAdd add = new MyAdd(ComputeSum);
    Console.WriteLine("시작 전");

    IAsyncResult result = add.BeginInvoke(7, 8, null, null);
    Console.WriteLine("비동기 시작");

    while (!result.IsCompleted)
    {
        Console.Write("."); // 진행 표시
        Thread.Sleep(100); // CPU 절약
    }

    long output = add.EndInvoke(result);
    Console.WriteLine($"\n완료: {output}");
    Console.ReadLine();
}

3. 콜백 모드 (비차단)

비동기 호출 후 즉시 메인 스레드가 계속 실행되며, 작업 완료 시 등록된 콜백 함수가 자동 호출됩니다. 이 방식은 스레드 차단 없이 비동기 작업을 관리할 수 있는 강력한 패턴입니다.

static void Main(string[] args)
{
    MyAdd add = new MyAdd(ComputeSum);
    Console.WriteLine("작업 시작");

    IAsyncResult result = add.BeginInvoke(15, 3, new AsyncCallback(OnComplete), null);
    Console.WriteLine("다른 작업 수행 중...");

    Thread.Sleep(2000);
    Console.WriteLine("프로그램 종료 준비");
    Console.ReadLine();
}

static void OnComplete(IAsyncResult ar)
{
    Console.WriteLine("콜백 호출됨");

    // AsyncResult로 다운캐스팅
    AsyncResult asyncResult = (AsyncResult)ar;
    MyAdd callbackDelegate = (MyAdd)asyncResult.AsyncDelegate;

    // 최종 결과 획득
    long value = callbackDelegate.EndInvoke(ar);
    Console.WriteLine($"결과: {value}");
}

AsyncCallback 형식은 void 반환이며, IAsyncResult 하나의 매개변수를 받아야 합니다. 이를 위반하면 컴파일 오류가 발생합니다.

AsyncResult 클래스는 내부적으로 AsyncDelegate 속성을 통해 원래의 델리게이트를 참조하며, AsyncState 속성은 BeginInvoke 호출 시 전달된 커스텀 데이터를 포함합니다.

태그: 비동기 처리 스레드 풀 BeginInvoke EndInvoke AsyncCallback

6월 23일 01:56에 게시됨