Blocking, non blocking vs sync, async

Programming

Posted by jopemachine on December 31, 2021 Updated on October 20, 2022

Blocking, Non blocking vs Sync, Async

원래 syncblocking이고 asyncnon blocking 이라고 이해해두었으나, Node.js에서만 그렇게 작동하고, 원래 다른 개념이기 때문에 다시 정리해둔다.

Blocking vs Non blocking

Blocking

  • blocking은 말 그대로 파일을 읽거나 API 콜을 하는 등 특정 태스크가 끝날 때 까지 기다리는 방식이다.

  • blocking 함수는 함수가 리턴하는 시점에 태스크가 끝나 있다.

  • 병렬 구조 없이 blocking 함수가 리턴할 때 까지 기다리고만 있기 때문에 비효율적이고, 작동 방식이 단순하다. (다만 non-blocking이라고 해서 병렬적인 것은 아니다. concurrency와 parallelism의 차이 참고)

  • 기본적으로 function callblocking이다.

Non-blocking

  • 반면에 Non blocking 함수는 실행 흐름에 Multiplexing을 적용해 바로 리턴하고, 비동기 태스크 (오래 걸리는 작업)를 다른 스레드에서 처리하거나 (멀티 스레딩 언어) 태스크 큐에 넣어놓고 콜 스택이 비었을 때 처리되도록 한다 (브라우저 및 Node.js와 같은 이벤트 루프를 사용하는 런타임).

  • Non-blocking인 함수는 함수가 끝난 시점과 태스크가 무관하며, 콜백 함수를 태스크 큐에 등록해 놓는 식으로 처리를 위임한다. 위임된 콜백은 종종 polling (IOCP, epoll 등)을 통해 구현된다.

Sync vs Async

  • Sync는 예측 가능한 시점에, 예측 가능한 순서대로 실행된다. 예측 가능한 순서란 것은, 함수 내에서 실행 흐름의 주도권이 계속 본인에게 있다는 것을 의미하며, 리턴 값을 받아서 바로 처리한다.

  • Sync는 System call이 리턴되는 시점에 function call도 리턴.

  • 반면 Async는 순서가 정해져 있지 않다. 태스크에 관심이 없고, 실행 흐름을 런타임에 양도한다.

  • Async는 태스크에 관심이 없기 때문에 작업이 끝나지 않은 상태에서 function call이 그냥 리턴된다. 작업은 프라미스, 콜백 함수 등의 형태로 비동기 태스크를 위임한 런타임에서 처리된다.

Sync blocking

  • 태스크를 수행하면서 단순히 끝나기를 기다리기만 해, 해당 스레드의 수행이 block 된다.

  • 커널 관점에서 바라본다면, 호출된 System call이 끝날 때 까지 caller는 계속 기다려야하며, System call이 끝나야 function call이 리턴되고, 리턴 받은 값으로 다음 명령어가 실행되게 된다.

Sync non-blocking

  • Multiplexing을 통해 폴링(polling) 하면서 다른 작업을 처리한다. 작업이 처리되면 caller에서 다음 작업을 수행하기 때문에 sync (태스크가 위임되지 않았다.), 작업이 수행되는 동안 다른 작업을 수행할 수 있기 때문에 non-blocking이다.

  • epoll, select가 대표적인 Sync non-blocking 함수.

Async non-blocking

  • 태스크를 등록해놓고 다른 작업을 처리 함.

  • Node.js, 브라우저에서 프라미스에 then 콜백 함수를 위임해놓고 다른 일 처리를 실행하는 것. caller가 콜백 함수(태스크)에 관심이 없으므로 async, 호출한 이후 다른 작업을 수행할 수 있기 때문에 non-blocking이다.

  • WinAPI에선 IOCP가 대표적인 Async non-blocking.

Async blocking

  • async 함수를 호출해 Async blocking인 줄 알았는데 내부에서 Sync blocking function을 호출해서 실은 blocking인 경우.

async, await을 사용해 동기적으로 동작시키기 (javascript)

1
2
3
4
5
6
(async function () {
  await fs.promises.readFile('a');
  console.log('blocking');
}) ();

console.log('non-blocking');
  • 태스크를 수행하면서 해당 function call 아래 라인은 block 된다.

  • 즉, 해당 function 내부에서 block 되는 것 처럼 보이지만, 비동기 처리가 되어 있기 때문에, (브라우저) 이벤트 처리 등 애플리케이션은 다른 일을 처리할 수 있다.

  • await 구문을 적용하면 function call이 끝난 시점에 태스크가 끝나게 된다, 즉 await 구문은 async 함수를 sync로 보이게 만들어 줌으로써 caller 구현부를 단순하게 만들어준다.