.NET APM 비동기 호출의 Begin...과 End... 조합
.NET의 APM(Asynchronous Programming Model) 비동기 호출 패턴은 Begin과 End 쌍으로 이뤄집니다. 일반적으로, 이 쌍은 어떻게든지 맞춰주는 것이 좋습니다. 심지어, End 메서드의 호출 시에 예외가 발생한다고 해도 마찬가지입니다.
예를 하나 들어볼까요?
string host = "192.168.0.95";
int port = 25000;
int timeout = 500;
while (true)
{
Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = _socket.BeginConnect(host, port, (ar) =>
{
}, null);
if (result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout), false) == false)
{
continue;
}
_socket.EndConnect(result);
break;
}
위의 코드는 서버에 연결이 될 때까지 재시도를 하는 코드입니다. 재미있는 것은 여기서 WaitOne이 지정된 Timeout이 지나 EndConnect를 호출하면 예외가 발생합니다. 왜냐하면 연결이 될 수 없는 상황이기 때문인데, 그로 인해 EndConnect를 호출하지 않고 다시 재시도를 하는 코드로 넘어가고 있습니다. 표면상으로 보면 문제가 없을 듯 싶은데 실제로 이 프로그램을 실행하고 "netstat -ano" 명령어를 통해 해당 프로세스에 속한 소켓 포트를 확인해 보면 약 40여개의 포트가 잠식되고 있는 것을 볼 수 있습니다.
TCP 220.152.82.220:33382 192.168.0.95:25000 SYN_SENT 5636
TCP 220.152.82.220:33383 192.168.0.95:25000 SYN_SENT 5636
...[생략]...
TCP 220.152.82.220:33421 192.168.0.95:25000 SYN_SENT 5636
TCP 220.152.82.220:33422 192.168.0.95:25000 SYN_SENT 5636
TCP 220.152.82.220:33423 192.168.0.95:25000 SYN_SENT 5636
더욱 재미있는 것은, 지정된 IP(본문에서는 192.168.0.95)에 해당하는 컴퓨터가 있는 경우에는 (연결 포트로 대기하는 프로그램이 없어도) 2~3개의 소켓 포트만 살아 있는 것을 볼 수 있습니다.
반면, 예외가 발생한다고 해도 아래와 같이 EndConnect를 명시적으로 호출해주면 어떤 상황에서도 1개의 소켓 포트만 SYN_SENT 상태로 사용되는 것을 확인할 수 있습니다.
while (true)
{
Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IAsyncResult result = _socket.BeginConnect(host, port, (ar) =>
{
}, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout), false);
try
{
_socket.EndConnect(result);
break;
}
catch { }
continue;
}
사실, Begin/End 쌍을 맞추라는 건 .NET APM 비동기 호출 패턴에서 명시하고 있는 내용이긴 합니다.
For each call to BeginOperationName, the application should also call EndOperationName to get the results of the operation.
하지만 위의 내용만으로 보면, "to get the results of the opration"이라고 써 있으므로 결과가 필요없으면 호출하지 않아도 될 것같은 의미를 담지만, 결과는 물론이고 심지어 예외가 발생하는 명백한 상황일지라도 Begin/End 쌍을 반드시 맞춰주는 것이 좋습니다.
(
첨부한 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]