머리로 이해하고 마음으로 느껴야하는 토픽인 것 같다--! 내 나름대로 이해한 것을 이해의 과정에 따라 정리해보겠다.
1. 개념적 차이
1) Blocking vs Non-Blocking : 호출하는 쪽에서 "블락(막힘)" 당하는가?
블로킹은 보통 "막히다"라는 뜻으로 쓰인다. 배구에서 스파이크를 치려는데 블락 당하거나, 특정 사이트의 접근이 블락 당하거나..
이런 의미에서 Blocking / Non-Blocking은 호출하는 쪽에서 일을 할 수 있냐의 여부에 초점이 맞춰져 있다.
- Blocking이라면 블락당해서 결과가 전해질 때까지 호출하는 쪽은 아무것도 하지 못한 채 대기하고,
- Non-Blocking이라면 결과의 여부와 상관 없이 호출하는 쪽에선 무언가를 할 수 있다.
여기서 무언가를 한다는 것은 "제어권 존재의 여부"를 의미한다. 즉, 블로킹은 호출과 동시에 프로세스의 제어권을 호출하는 쪽으로 넘기고, 논블로킹은 그러지 않는다는 뜻이다. 캐주얼하게 호출한 쪽의 자유가 있냐? 라고 생각하면 될 것 같다.
2) Synchronous vs Asynchronous : 호출하는 쪽에서 호출되는 쪽을 신경쓰는가?
Synchoronous는 우리말로 "동기화" 즉, 같은 시간에 함께 하는 것으로 맞추는.. 그 얼추 비슷한 것을 뜻한다. Synchronous에서 가장 중요한 것은 단어 그대로 작업들이 순차적으로 처리되어야한다는 점이다. 따라서 함수 A가 함수 B를 호출할 때, sync 상황에선 동작의 동기화를 위해 B함수의 반환값과 순서를 신경쓰게 된다.
- Sync라면 호출한 쪽에서 호출된 쪽의 함수 실행 결과와 순서를 신경쓴다. 실행 결과를 호출한 쪽에서 처리해야하기 때문이다.
- Async라면 호출한 쪽에선 호출된 쪽에 관심이 없다. 실행 결과는 호출된 쪽이 콜백으로 알아서 처리하기 때문이다.
상위 프로세스가 여러개의 I/O 관련 프로세스를 실행시키는 상황에서, async에선 어떤 입출력이 먼저 반환되도 신경쓰지 않는다. 애초에 입출력 결과를 콜백으로 자기들이 알아서 처리하는데 그럴 필요가 없는 것은 당연하다. 반대로 Sync에선 IO의 결과값을 받아 처리해야하므로 호출한 순서대로 반환을 받는 것이 중요하다.
2. 동작 차이
1) Blocking VS Non-Blocking : 호출되는 함수가 바로 return하는가?
블로킹과 논블로킹은 블락당하냐의 여부, 즉 제어권이 없고 있고의 차이이다. 제어권은 곧 return이다. 어떤 함수가 다른 함수를 호출했을 때, 각각의 경우에 일어나는 동작의 차이를 생각해보자.
- A가 B를 호출했다.
- Blocking : B는 호출되자마자 바로 return한다. return받은 A는 자신의 작업을 이어간다.
- Non-Blocking : B는 작업이 끝날 때까지 return하지 않는다. return이 있어야만 다음 코드 라인을 이어갈 수 있기에, A는 B가 return 될때까지 대기한다.
2) Sync VS Async : 작업 결과를 누가 처리하는가?
호출당하는 쪽의 결과값을 누가 처리하냐의 차이가 sync와 async를 구분짓는다. 이 역시 A가 B를 호출하는 예시를 통해 알아보자.
- A가 B를 호출했다.
- Sync : B가 실행완료된다. A는 B의 결과값을 받아 후처리를 한다.
- Async : B가 실행완료된다. B는 콜백 함수를 통해 자신의 실행 결과의 후처리를 한다.
Asynchronous에선 실행 결과를 호출 당하는 쪽에서 처리하므로 호출하는 쪽에선 호출 뒤 상태가 어떤지 알 필요가 없다. 동작이 이렇게 진행되기 때문에 개념적으로 "호출한 쪽에서 신경을 쓰지 않는다"로 이해할 수 있는 것이다. 어떤 프로세스에서 여러개의 IO 작업을 호출해야하는 경우, sync라면 먼저 호출된 IO작업의 결과가 반환될 때까지 기다린 뒤 다음 작업을 호출하지만 async는 그러지 않는다. 그저 호출 딱딱딱! 하고 자신의 작업을 이어갈 뿐이다.
3. 4개 조합
다시 한 번 정리하면, 블로킹/논블로킹은 리턴이 즉시 되어 제어권을 반환받는가? 의 차이이고 sync/async는 결과값을 호출하는 쪽에서 처리하는가? 의 차이이다. 사실 sync + blocking이 비슷하게 보이고 async + non-blocking이 비슷하게 보인다. 찾아보니 대체적으로 많은 프로그래밍 언어와 기능들이 둘의 조합으로 이뤄지는듯 하다. 하지만 굳~이 정확하게 뜻을 구분지어서 동작원리를 나눠 설명하면 구분되는 개념인 것.
어쨌든, sync와 async일 때, block과 non-block일 때 각각의 조합에서 프로세스의 실행 흐름이 어떻게 되는지 그림과 함께 살펴보자.
1) Sync + Blocking
다른 함수를 호출할 때, 호출한 함수가 실행 완료까지 대기하고, 호출된 함수의 return값을 호출한 쪽에서 처리하는 패턴이다. 일반적으로 생각할 수 있는 흐름이다.
2) Async + Non-Blocking
Non-blocking이므로 호출된 함수가 바로 return하여 제어권을 반환한다. 호출한 함수는 원래 일을 계속 이어간다. async이므로 호출한 함수는 호출된 함수의 결과값을 신경쓰지 않는다. 호출된 쪽에서 callback까지 처리한 뒤 호출한 쪽에 알릴 뿐이다. (아니면 그냥 작업 완료 notification을 보내거나)
# Asynchronous != Multi threading
async를 구현하기 위해선 멀티스레딩이 필수로 보이지만 사실 그렇지 않다.
물론 멀티스레드로 구현하는 것이 일반적이다. 그러나 호출되는 함수가 I/O 관련 작업이라면 Non-Block I/O가 가능한 경우 싱글스레드여도 async programming이 가능하다.
3) Sync + Non-Blocking
Non-Blocking이기 때문에 호출하는 쪽은 호출과 동시에 제어권을 반환받지만, Sync이기 때문에 호출된 쪽의 결과값을 신경써야 한다. 결과값을 호출하는 쪽에서 처리해야하기 때문이다. 따라서 호출한 쪽에선 제어권을 받았음에도 호출된 쪽에게 계속 작업의 완료 여부를 묻게된다. 그리고 작업이 완료되었을 때, 비로소 그 값을 받아 일정 로직을 수행한다.
4) Async + Blocking
호출한 쪽에선 호출된 쪽의 반환값에 관심도 없는데 block되는 이상한 구조이다.
장점도 없어보이고 이 환경을 사용할 이유는 없어보이는데.. 이런 경우가 어떨때 가능할까? 찾아보니 nodejs + MySQL 조합일 때 위와 같은 문제가 발생할 수 있다고 한다. 그러니까, async 환경에서 block 기반으로 동작하는 프로그램 컴포넌트를 사용할 때이다. non-block으로 작동하길 원하지만 어쩔 수 없는 상황에서 이같은 상황이 발생하는 것인가..
4. 결론
헷갈리지 말자!
Block / Non-Block은 제어권을 바로 돌려받느냐 마느냐, 즉 호출된 쪽이 바로 return하느냐의 차이이다.
Sync / Async은 반환값을 신경쓰느냐, 즉 호출한 쪽에서 호출된 쪽의 작업 완료 값을 순차적으로 받아야하냐의 차이이다.
Reference)
http://homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/
https://www.youtube.com/watch?v=EJNBLD3X2yg&t=10s