반응형
C# APM(Asynchronous Programming Model) 패턴
APM(Asynchronous Programming Model)은 비동기 프로그래밍을 구현하는 오래된 패턴으로, .NET Framework 초기에 설계되었습니다. 이 패턴은 Begin/End 메서드 쌍을 사용하여 비동기 작업을 수행하고, 콜백 또는 IAsyncResult를 통해 결과를 처리합니다.
APM의 주요 개념
- Begin/End 메서드 쌍:
- 비동기 작업을 시작하는 BeginXXX 메서드와 작업을 완료하고 결과를 가져오는 EndXXX 메서드로 구성됩니다.
- 예: FileStream.BeginRead()와 FileStream.EndRead().
- IAsyncResult 인터페이스:
- BeginXXX 메서드는 비동기 작업의 상태를 나타내는 IAsyncResult 객체를 반환합니다.
- 주요 속성:
- AsyncState: 사용자 정의 상태 객체.
- AsyncWaitHandle: 작업이 완료될 때 신호를 설정하는 이벤트 핸들.
- CompletedSynchronously: 작업이 동기적으로 완료되었는지 여부.
- IsCompleted: 작업이 완료되었는지 여부.
- 콜백 메서드:
- 비동기 작업이 완료되었을 때 호출되는 메서드입니다.
- BeginXXX 메서드에 콜백을 전달하여 작업 완료 시 로직을 실행합니다.
APM의 기본 구조
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
byte[] buffer = new byte[1024];
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
// BeginRead 시작
fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), new AsyncState(fs, buffer));
Console.WriteLine("Reading file asynchronously...");
}
// 콜백 메서드
static void ReadCallback(IAsyncResult ar)
{
AsyncState state = (AsyncState)ar.AsyncState;
FileStream fs = state.FileStream;
byte[] buffer = state.Buffer;
// 작업 완료 후 결과 가져오기
int bytesRead = fs.EndRead(ar);
Console.WriteLine($"Read {bytesRead} bytes.");
// 리소스 정리
fs.Close();
}
}
// 사용자 정의 상태 클래스
class AsyncState
{
public FileStream FileStream { get; }
public byte[] Buffer { get; }
public AsyncState(FileStream fileStream, byte[] buffer)
{
FileStream = fileStream;
Buffer = buffer;
}
}
APM의 특징
- 장점:
- 비동기 작업을 지원하며, 동시 작업 처리를 용이하게 만듭니다.
- .NET 초기부터 제공되어 안정성이 검증된 패턴입니다.
- 단점:
- 코드가 복잡하고 가독성이 떨어질 수 있습니다.
- 콜백 중첩으로 인해 관리가 어렵습니다.
- 현대적인 비동기 프로그래밍 스타일에 비해 유지보수가 어렵습니다.
현대적인 대안
.NET 4.5 이후로, APM은 점차 **TAP(Task-based Asynchronous Pattern)**으로 대체되었습니다. TAP은 async/await 키워드를 사용하여 비동기 작업을 보다 쉽게 관리할 수 있도록 설계되었습니다.
예를 들어, 위의 APM 코드는 TAP에서 다음과 같이 간단히 표현할 수 있습니다:
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string filePath = "example.txt";
byte[] buffer = new byte[1024];
using FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
Console.WriteLine($"Read {bytesRead} bytes.");
}
}
APM은 여전히 일부 오래된 API에서 사용되지만, 새로운 코드에서는 TAP 사용을 권장합니다.
APM을 TAP으로 변환하는 방법과 APM의 실제 예제
1. APM을 TAP(Task-based Asynchronous Pattern)으로 변환하기
.NET에서는 TaskFactory.FromAsync 메서드를 통해 APM 기반 메서드를 TAP 기반으로 변환할 수 있습니다.
다음은 APM 메서드(BeginXXX, EndXXX)를 TAP 스타일로 변환하는 예제입니다.
코드 예제 (APM -> TAP 변환)
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string filePath = "example.txt";
byte[] buffer = new byte[1024];
using FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
// APM을 TAP으로 변환
int bytesRead = await Task.Factory.FromAsync(
fs.BeginRead, // Begin 메서드
fs.EndRead, // End 메서드
buffer, // 버퍼
0, // 오프셋
buffer.Length, // 길이
null // 상태 객체
);
Console.WriteLine($"Read {bytesRead} bytes.");
}
}
코드 설명
- Task.Factory.FromAsync는 BeginXXX와 EndXXX 메서드를 받아 TAP 스타일의 Task 객체를 생성합니다.
- await 키워드를 사용하여 비동기 작업을 간단하게 처리할 수 있습니다.
2. APM의 실제 예제
APM 예제 코드
다음은 파일을 비동기로 읽는 APM 코드입니다.
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
byte[] buffer = new byte[1024];
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
// BeginRead로 비동기 작업 시작
fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), new AsyncState(fs, buffer));
Console.WriteLine("Reading file asynchronously...");
Console.ReadLine(); // 콜백 완료를 기다리기 위해 콘솔 대기
}
// 콜백 메서드
static void ReadCallback(IAsyncResult ar)
{
AsyncState state = (AsyncState)ar.AsyncState;
FileStream fs = state.FileStream;
byte[] buffer = state.Buffer;
// 작업 완료 후 결과 가져오기
int bytesRead = fs.EndRead(ar);
Console.WriteLine($"Read {bytesRead} bytes.");
// 리소스 정리
fs.Close();
}
}
// 사용자 정의 상태 클래스
class AsyncState
{
public FileStream FileStream { get; }
public byte[] Buffer { get; }
public AsyncState(FileStream fileStream, byte[] buffer)
{
FileStream = fileStream;
Buffer = buffer;
}
}
실행 흐름
- FileStream.BeginRead 메서드로 비동기 읽기를 시작합니다.
- 작업이 완료되면 ReadCallback이 호출됩니다.
- FileStream.EndRead를 호출하여 읽은 데이터를 가져옵니다.
- 파일 스트림을 닫아 리소스를 정리합니다.
APM과 TAP 비교
특징 APM TAP
코드 복잡도 | 복잡 (콜백 메서드 필요) | 간단 (async/await 사용) |
가독성 | 낮음 | 높음 |
지원 범위 | .NET 초기 API | .NET 4.5+ API |
유지보수 용이성 | 어렵다 | 쉽다 |
3. 두 패턴 비교 예제
APM 코드
fs.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(ReadCallback), state);
int bytesRead = fs.EndRead(ar);
TAP 코드
int bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length);
반응형
'C#' 카테고리의 다른 글
Windows 공유 폴더 동시 연결 제한 해결 방법 (0) | 2025.01.17 |
---|---|
C# `ref`와 `out` 키워드의 차이점과 활용법 (2) | 2025.01.16 |
`TimeSpan.FromSeconds` vs `Thread.Sleep`: 차이와 올바른 사용법 (0) | 2025.01.16 |
ProcessorAffinity로 CPU 코어 활용 제어하기 (28) | 2025.01.14 |
C# 람다식: 간결하고 강력한 익명 함수 이해하기 (0) | 2025.01.13 |
C# LINQ: 데이터 쿼리를 간결하고 강력하게 다루는 방법 (0) | 2025.01.13 |
SocketException: '각 소켓 주소는 하나만 사용할 수 있습니다' 에러 원인 및 해결법 (1) | 2025.01.10 |
C# internal: 어셈블리 내 접근 제한자 (0) | 2025.01.10 |