[기초] Blocking I/O vs Non-Blocking I/O

컴퓨터의 기본 동작중에 I/O는 가장 느리다. RAM에 접근하는데에는 나노초가 걸리지만, 디스크와 네트워크에 접근하는 데에는 밀리초가 걸린다. 실제 CPU의 측면에서는 I/O가 많은 비용을 요구하지 않지만, 보내지는 요청과 작업이 완료되는 순간 사이의 지연이 발생하게 되는 것이다.

Blocking I/O vs Non-Blocking I/O

I/O는 컴퓨터의 기본적인 동작 중에서 가장 느린 동작이다. 전통적인 Blocking I/O 프로그래밍에서는 I/O 요청에 해당하는 함수 호출은 작업이 완료될 때까지 스레드를 차단했다. 당연히 블록킹 I/O가 구현된 웹 서버는 여러 연결을 처리할 수 없다는 것이다. 물론 여기에 대한 대안으로 스레드를 늘려서 처리하는 방법이 있겠지만, 메모리를 소모하고 콘텍스트를 유발하는 스레드는 값싼 자원이 아니기 때문에 효율적인 방법이 아니다.

Non-Blocking I/O의 경우 최신 운영체제에서 대부분 지원하는 리소스에 접근하는 새로운 매커니즘이다. Non-Blocking을 구현하는 여러 가지 패턴이 있는데, 가장 기본적인 패턴은 busy-waiting이다. busy-waiting은 실제 데이터가 반환될 때까지 루프 내에서 리소스를 폴링하는 것이다.

// NOTE: pseudocode
function busyWaiting() {
    while(something === true){
    	// waiting ...
    }
 }

위에 슈도 코드를 봐도 정말 비효율적이라는 게 느껴진다. 사용할 수 없는 리소스를 반복하는데만 cpu 사이클이 계속 사용되고 있는 것이다. 이러한 문제에 대한 대안으로 필요한 것이 Event-Demultiplexing이다. 이벤트 디멀티플랙서(Event-Demultiplexer)는 I/O 이벤트를 수집하여 **이벤트 큐(Queue)**에 넣고 처리할 수 있는 새 이벤트가 있을 때까지 차단한다. 이 패턴을 사용하면 busy-waiting을 사용하지 않고 단일 스레드 내에서 여러 I/O 작업을 처리할 수 있다.출처:

또한 작업이 여러 쓰레드에 분산되는 것이 아닌, 시간이 따라 분산되어, 스레드의 유휴 시간이 위와 같이 최소화할 수 있다.

Last updated