본문 바로가기
프로그래밍/C#

C# 비동기 프로그래밍 APM이란 무엇인가?

by bantomak 2024. 11. 21.
반응형

APM(Asynchronous Programming Model)이란?

APM(Asynchronous Programming Model)은 C#에서 비동기 작업을 구현하는 모델로, .NET Framework에서 도입되었다. 비동기 패턴으로 동작하며, 주로 Begin 혹은 End 이름으로 시작하는 메서드의 쌍을 사용하여 작업을 비동기적으로 처리한다.

 

해당 모델은 .NET Framework 초기에 도입된 모델로 지금은 TAP(Task-Based Asynchronous Pattern)이 도입된 이후로는 사용되지 않는 추세이다. 간략하게만 알아보고 C# 비동기 작업에서 주로 사용하게 될 TAP에 대해서도 알아보자.

APM의 주요 특징

  • 비동기 작업 처리 : 작업을 시작할 때 Begin 메서드를 호출하고, 작업이 완료되면 End 메서드를 호출하여 결과를 처리한다.
  • 콜백(Callback) 기반 비동기 처리 : 작업 완료시 지정한 콜백 메서드가 호출된다.
  • 작업 상태 확인 : 작업 상태는 IAsyncResult 인터페이스를 통해서 관리된다.

주요 메서드

Begin으로 시작하는 메서드 (예시 : BeginSend 메서드)

  • 비동기 작업을 시작한다.
  • 작업 상태를 나타내는 IAsyncResult를 반환한다.
  • 콜백 메서드(delegate)를 전달하여 작업 완료 시 호출되도록 한다.

End로 시작하는 메서드 (예시 : EndSend 메서드)

  • 작업이 완료된 후 결과를 가져온다.
  • IAsyncResult를 입력으로 받아 작업의 결과를 처리한다.

IAsyncResult 인터페이스

  • 비동기 작업의 상태를 추적한다.
  • 작업의 완료 여부, 결과 등을 확인할 수 있다.

APM으로 구현한 데이터 전송 서버 예제

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace APMServer
{
    class MySocket
    {
        public Socket m_socket;
        public byte[] m_data = new byte[1024];
    }

    class Program
    {
        private static Socket serverSocket;

        static void Main(string[] args)
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, 12345));
            serverSocket.Listen(10);

            Console.WriteLine("서버 시작, 클라 대기....");

            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
            Console.ReadLine();
        }

        private static void AcceptCallback(IAsyncResult result)
        {
            Socket clientSocket = serverSocket.EndAccept(result);
            Console.WriteLine("클라 접속 완료");

            MySocket ms = new MySocket();
            ms.m_socket = clientSocket;
            ms.m_data = new byte[1024];

            clientSocket.BeginReceive(ms.m_data, 0, ms.m_data.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), ms);
        }

        private static void ReceiveCallback(IAsyncResult result)
        {
            MySocket ms = (MySocket)result.AsyncState;
            int bytesReceived = ms.m_socket.EndReceive(result);

            Console.WriteLine($"클라에서 받은 메세지 : {Encoding.UTF8.GetString(ms.m_data, 0, bytesReceived)}");

            string response = "Hello, world";
            byte[] responseBytes = Encoding.UTF8.GetBytes(response);
            ms.m_socket.BeginSend(responseBytes, 0, responseBytes.Length, SocketFlags.None, new AsyncCallback(SendCallback), ms.m_socket);
        }

        private static void SendCallback(IAsyncResult result)
        {
            Socket clientSocket = (Socket)result.AsyncState;
            int bytesSent = clientSocket.EndSend(result);

            Console.WriteLine($"{bytesSent} bytes sent");
        }
    }
}

APM으로 구현한 데이터 전송 클라이언트 예제

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace APMClient
{
    class Program
    {
        private static Socket clientSocket;

        static void Main(string[] args)
        {
            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            clientSocket.BeginConnect(new IPEndPoint(IPAddress.Loopback, 12345), new AsyncCallback(ConnectCallback), null);

            Console.ReadLine();
        }

        static void ConnectCallback(IAsyncResult ar)
        {
            clientSocket.EndConnect(ar);
            Console.WriteLine("서버 연결 완료");

            string message = "hello, server";
            byte[] messagebytes = Encoding.UTF8.GetBytes(message);

            clientSocket.BeginSend(messagebytes, 0, messagebytes.Length, SocketFlags.None, new AsyncCallback(SendCallback), null);
        }

        static void SendCallback(IAsyncResult ar)
        {
            int bytesSent = clientSocket.EndSend(ar);
            Console.WriteLine($"서버로 바이트 {bytesSent} 만큼 전송");

            byte[] buffer = new byte[1024];
            clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), buffer);
        }

        static void ReceiveCallback(IAsyncResult ar)
        {
            var buffer = ar.AsyncState as byte[];
            int bytesReceive = buffer.Length;

            Console.WriteLine($"서버로 부터 받은 메세지, {Encoding.UTF8.GetString(buffer, 0, bytesReceive)}");
        }
    }
}

APM 장단점

장점

  • 효율적인 비동기 작업 : I/O 작업 중 스레드 블로킹이 발생하지 않음
  • 콜백 기반 처리 : 작업 완료 시점에서 콜백으로 등록한 매서드를 실행한다.

단점

  • 복잡한 코드 흐름 : 콜백 중첩으로 인해 코드가 복잡해지고 가독성이 떨어질 수 있음
  • 에러 처리 어려움 : 여러 단계의 콜백에서 예외 처리하는 것이 어렵고 추적이 까다로움
  • 최신 비동기 방식과의 비교 : .NET 4.5 이후에 추가된 async/wait 기반 TAP(Task-based Asynchronous Pattern)에 비해 사용이 어렵고 비효율적이다.

함께 읽으면 좋은 글

 

C# 비동기 프로그래밍 TAP이란 무엇인가?

TAP(Task-based Asynchronous Pattern)이란?TAP(Task-based Asynchronous Pattern)은 .NET Framework 4.5에서 비동기 프로그래밍을 단순화하기 위해 도입된 패턴이다. TAP은 Task 및 Task 클래스를 기반으로 비동기 작업을 처

jettstream.tistory.com

댓글