요번에 책을 읽으면서 Blocking I/O와 Non-Blocking I/O에 대해서 궁금증이 생겨 자세한 내용을 찾아보게 되었다. 예전에 Node.js가 Non-Blocking I/O라는 말을 들었는데 그땐 정확히 이해하지 못한채 넘어갔던 것 같다. 이번 기회에 확실히 하고자 한번 찾아보았다.
우선적으로 I/O란 말을 프로그래밍을 하다보면 자주 접할 것이다. I/O란 Input, Output을 의미하는 말로, 대부분 키보드 입력, 모니터 출력 이런식으로 단순하게 생각하지만 사실 프로세서와 메모리, 확장 슬롯, 마더보드에서 일어나는 데이터 전송도 I/O라고 일컬어지고, 네트워크를 통해 데이터를 송수신 하는 것도 I/O라고 칭한다.
또한 I/O는 CPU 연산 속도에 비해 매우 느리다. 따라서 이를 어떻게 관리하느냐에 따라 어플리케이션의 성능이 좌지우지 된다.
그러면 Blocking I/O란 무엇일까?
I/O 작업이 진행 되는 동안 프로세스가 작업을 중단한 채 I/O가 끝날때까지 대기하는 방식을 의미한다
한번 그림을 보자.

위 그림과 같이 어플리케이션이 커널(Kernel)에 시스템 콜(System Call)을 하고 입출력 처리가 완료 될때까지 대기한다. 즉, 제어권을 넘겨준다.
여기서 System Call이란 어플리케이션이 커널의 기능을 사용하기 위해 커널에 요청하는 것을 의미한다.
만일 단일 스레드라면, 입출력이 완료 될때까지 멈춰 있는다.
멀티 스레드 환경에서 I/O 작업으로 실행 중인 스레드가 멈추게 되면 CPU는 문맥 교환(Context Switching)을 통해 다른 스레드에게 CPU 자원을 할당 시킨다. 이 과정에서 문맥 교환이 잦으므로 오버헤드가 발생하여 성능이 떨어지게 된다.
직감적으로 Non-Blocking I/O는 위에서 살펴본 Blocking I/O의 반대라는 것을 예상할 수 있다. Non-Blocking I/O는 I/O 작업이 끝나지 않아도 제어권을 다시 돌려 받는다.
제어권을 돌려 받은 프로그램은 일정 주기마다 I/O 작업이 완료 됐는지 확인을 하는데, 이를 Polling 이라고 한다.
스레드는 중단 없이 계속 실행되기 때문에 문맥 교환이 필요하지 않다.

Non-Blocking과 Blocking의 차이는 다른 주체가 작업할 때 물리적으로 자신의 제어권이 있는지가 기준이다.
Blocking, Non-Blocking I/O를 살펴보다 보면 자연스럽게 Sync, Async에 대한 개념도 나온다. 이들이 무슨 차이가 있는지 알아보자.
Blocking, Non-Blocking I/O는 제어권이 어디에 있느냐가 관건이였다면, Sync와 Async는 작업들의 순서가 보장 되는지, 아닌지가 관건이다.

Sync 방식에서는 함수 A의 작업이 끝날 때까지 함수 B는 대기하고, A가 끝나야 B가 실행된다. 마찬가지로 C도 같은 방식으로 동작한다.
즉, 작업을 수행할 때 해당 작업의 완료 여부를 신경쓴다.
이는 Blocking 방식과 유사하지만 작업 순서가 보장된다는 것에서의 관점의 차이가 있다.

Async 방식은 A 함수의 완료 여부와 상관 없이, B 함수가 동작한다. C도 마찬가지이다.
작업 완료 여부를 신경쓰지 않으므로, 동시적으로 실행될 수 있다.
내가 이해한 바로는 Sync와 Blocking, Async와 Non-Blocking은 거의 비슷한 내용이지만 얼마나 포괄적이냐의 차이 같다.
Blocking과 Non-Blocking은 특정 작업에 대해 어떻게 처리할 것인지의 차이이다. 예를 들면, 식당에서 음식을 주문한다 하면, 음식을 주문하고 음식을 받는 것을 어떻게 처리할 것인지이다.
Blocking: 음식을 주문하고 음식이 나올때까지 기다린다.
Non-Blocking: 음식을 주문하고 나올때까지 친구와 수다를 떨다 음식을 받는다.
Sync와 Async는 음식을 주문하고, 식사를 하는 과정을 어떻게 처리할 것인지이다.
Sync: 음식을 주문하고 기다리다가 음식이 나오면 식사를 한다.
Async: 음식을 주문하고 다른 일을 하고 있다가 직원이 알려주면 음식을 받아 식사를 한다.
좀 모호할 수 있지만, 정리 하자면,
블로킹은 '한 작업의 내부에서 어떻게 처리할 것인가'에 초점을 맞추고,
동기는 '여러 작업들 사이의 진행 순서'에 관한 방식을 의미한다.
이미지 출처: https://velog.io/@hotdari90/Async-Sync-Blocking-Non-Blocking