비동기 처리에서 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 호출 시 전달된 커스텀 데이터를 포함합니다.