[CS] 동기와 비동기, 블로킹과 논블로킹 해부하기

유아 Yooa·2023년 10월 24일
5

CS

목록 보기
1/4
post-thumbnail

Overview

최근 프로젝트를 진행하며 외부 API를 호출하여 응답값을 활용해야 하는 기능을 구현한 적이 있다. 한번의 요청에서 외부 API 여러 개를 호출하는 구현이 필요했는데, 이를 동기적으로 구현하니 크게는 10초의 지연이 발생했다. 비동기적인 호출 방식으로 변경하며 응답 시간을 크게 단축시킬 수 있었다.

필자는 비동기를 '동시성의 특징을 가진 기술' 정도로만 이해했으므로 Non-Blockin과도 같은 의미로 사용해왔다. 문득 사용한 기술의 특징이 Non-Blocking이라고 명시되어 있어 'Asynchronous와 무슨 차이가 있는거지?' 라는 의문이 생겼다.

오늘은 그 의문을 해결해보자.


개념의 오해

나를 포함해 대부분의 사람들은 동기와 비동기, 블로킹과 논블로킹에 대하여 비슷한 개념 정도로 오해를 많이 하고 있다고.👀

사실 동기와 비동기, 블로킹과 논블로킹이라는 두 개념의 형태가 비슷하지만, 서로 다른 관심사를 갖고 작업 수행 방식을 설명한다고 한다.

간단한 요약부터 보자면

  • 동기와 비동기(Sync and Async) : 요청 작업에 대한 완료 여부를 확인하여 작업을 순차적으로 수행할지 아닌지에 따라 결정되는 개념
  • 블로킹과 논블로킹(Block and Non-Block) : 현재 진행중인 작업이 블락(대기)되느냐 안되느냐에 따라 다른 작업을 수행할 수 있는지가 결정되는 개념

IBM Article

2006년 IBM DeveloperWorks Library에 Article로 게시된 Boost application performance using asynchronous I/O Linux Kernel에서 비동기IO(Asynchronous IO; AIO)를 소개하고 권장하고 있다. 동기적IO(Sync IO) 사용에서 종종 다른 프로세스 IO 요청이 overlap 되는 문제 현상에 의하여 필요성을 느꼈다고 한다.

당시 아티클에 게재된 unix에서 사용 가능한 4개의 I/O 모델이 나와 있는 도표이다.
지금의 우리에게는 다소 익숙한 개념들이 등장하는데, 2006년 당시에는 최근의 기술로 추천받고 권장하는 분위기이니 어색할 따름이다.

도표로 확인 가능하다 싶이 Synchronous/Asynchronous는 Blocking/Non-blocking은 개념적으로 차이가 존재하고 이들을 혼합한 모델은 기능적인 차이마저 존재한다.


Blocking/Non-blocking

다른 요청의 작업을 처리하기 위해 현재 작업을 차단하는, 대기하는 상태를 말한다. 대기하느냐 안하느냐의 유무를 나타내는 프로세스의 실행 방식.

  • Block
    • 호출된 함수(callee)가 할 일을 마칠 때까지 제어권을 가지며 호출한 함수(caller)에게 바로 돌려주지 않는다.
  • Non-Block
    • 호출된 함수(callee) 가 할 일을 채 마치지 않았더라도 바로 제어권을 건네주어(return) 호출한 함수(caller) 가 다른 일을 진행할 수 있도록 해준다.

제어권을 누가 갖느냐

제어권이란 프로세스의 실행 흐름을 제어할 수 있는 권리를 뜻한다. 즉, blocking과 non-blocking은 호출된 함수(callee)가 호출한 함수(caller)에게 제어권을 주느냐 안주느냐로 구분된다고.👀
제어권이 넘어가면 해당 스레드가 블로킹 된다고 생각하면 된다.

Blocking 방식의 제어권

A 함수가 B 함수를 호출하며 B에게 제어권을 넘긴다. (이때의 caller는 A가 되고, callee는 B가 되는 것.)

호출된 함수(callee)가 자신이 할 일을 모두 마칠 때까지 제어권을 계속 가지고서 호출한 함수(caller)에게 바로 돌려주지 않음.

에 따르면, B가 할 일을 마칠 때 까지 제어권을 가지고 함수를 실행하며 A에게 제어권을 바로 주지 않는 것. B가 할 일을 마치면 A에게 제어권을 주게 된다.
이때, A가 제어권을 넘겨주고 자신의 함수 실행을 멈춘 그 상태가 Block 되었다고 표현한다.

Non-Blocking 방식의 제어권

A 함수가 B 함수를 호출 할 때, 제어권을 넘기지 않는다.

호출된 함수(callee) 가 자신이 할 일을 채 마치지 않았더라도 바로 제어권을 건내주어(return) 호출한 함수(caller) 가 다른 일을 진행할 수 있도록 해줌.

에 따르면, B의 할 일이 마치치 않아도 A에게 제어권을 건네주어서 A가 다른 일을 계속 실행할 수 있게 한다. A가 제어권을 가지고 있는 형태가 되기에 Non-Block 형태라고 표현한다.

Synchronous/Asynchronous

요청한 작업에 대한 완료 여부를 따져서 순차적으로 처리할 지, 그대로 수행할지의 방식과 관련된다.

  • Synchronous
    • 호출된 함수(callee) 의 수행 완료 결과를 호출한 함수(caller) 가 따진다.
  • Asynchronous
    • 호출된 함수(callee) 의 수행 완료 결과를 호출된 함수(callee)가 따진다.

작업 순서 처리 차이

동기와 비동기의 차이는 여러 개의 요청 작업에 대하여 순차적으로 처리하느냐의 차이인데,
따라서 동기(Synchronous) 작업은 요청 작업에 대한 순서가 지켜지고, 비동기(Asynchronous)는 순서가 지켜지지 않는다.

예를 들어, 웹 애플리케이션에서 데이터베이스 쿼리를 수행하는 작업이 있다고 해보자.

  • 이 작업을 동기적으로 수행한다면?

    • 데이터베이스에서 응답이 올 때 까지 웹 애플리케이션은 다른 작업을 하지 못하고 기다린다.
    • 대규모 트래픽이 발생할 경우 성능이 저하된다.
  • 이 작업을 비동기적으로 수행한다면?

    • 데이터베이스 응답이 올 때까지 기다리는 동안에도 다른 작업을 '동시에 처리' 할 수 있다.
    • 대규모 트래픽에서도 안정적으로 동작한다.

이런 동시 처리(= 두 개 이상의 작업이 동시 실행되는)의 개념으로 멀티 스레드나 멀티 프로세싱 같은 방식이 구현된다.

요청 작업에 대한 완료 여부를 신경쓰지 않고 그 다음 작업을 수행하는 멀티 작업은 비동기 방식에서의 성능 향상을 가져온다.


비동기와 논블로킹 뭐가 다른데?

비동기적 방식과 논블로킹 방식이 추구하는 결과만 보자면 동시에 작업을 수행한다는 멀티 작업 측면에서 비슷하게 볼 수 있다. 실제로 코드 상에서 경계를 구분하기도 애매하다고.👀

그러한 이유로 실생활에서 혼용하여 사용하는게 대다수이다. 하지만 둘의 개념은 엄연히 구분되며 시점을 다르게 하여 기억하자.

비동기는 출력 순서와 관련된 개념이고, 논블로킹은 병렬 실행과 관련된 개념이다.


혼합해서 사용해보기

Non-Blocking & Sync

다른 작업이 진행되는 동안에도 자신의 작업을 처리하고(Non Blocking), 다른 작업의 결과를 바로 처리하여 작업을 순차대로 수행하는 (Sync) 방식

호출된 함수호출한 함수에게 바로 제어권을 건네주어서 실행을 이어가면 되지만, 호출된 함수의 완료 결과를 확인하느라(순차적으로 진행하느라) 함수 실행이 멈추게 된다. 결과적으로는 동기적으로 작업을 순서대로 실행한다.

Non-Blocking & Async

다른 작업이 진행되는 동안에도 자신의 작업을 처리하고(Non- Blocking), 다른 작업의 결과를 바로 처리하지 않아 작업 순서가 지켜지지 않는 (Async) 방식.

작업량이 많거나 시간이 오래 걸리는 작업을 처리해야 하는 경우에 적합하다.
대용량 데이터를 처리하거나 많은 요청을 처리하는 서비스에서는 Async Non Blocking 방식을 사용해 한 작업이 처리되는 동안 다른 작업을 처리할 수 있으므로 전체 처리 시간을 줄일 수 있어 애플리케이션 처리 성능을 향상시킬 수 있게 된다.

Blocking & Sync

다른 작업이 진행되는 동안 자신의 작업을 처리하지 않고(Blocking), 다른 작업의 완료 여부를 바로 받아 순차적으로 처리하는 (Sync) 방식이다.

순차적으로 실행되는 특성으로, 일반적으로 작업이 간단하거나 작업량이 적은 경우에 사용된다. 다른 작업의 결과가 자신의 작업에 영향을 주는 경우에 활용할 수 있다.

하지만 작업량이 많거나 시간이 오래 걸리는 작업을 처리해야 하는 경우는 작업이 끝날 때까지 다른 작업을 처리하지 못하므로, 전체 처리 시간이 오래 걸리게 되어 비효율적이다.

Blocking & Async

다른 작업이 진행되는 동안 자신의 작업을 멈추고 기다리는(Blocking), 다른 작업의 결과를 바로 처리하지 않아 순서대로 작업을 수행하지 않는(Async) 방식.

오히려 혼동스럽기에 실무에서 거의 다룰일이 없다.


Blocking Sync VS Non-Blocking Sync

결국은 순차적으로 수행하기에 차이가 없는 거 아닌가?🤔

→ 성능 차이는 상황에 따라 다르겠지만, 일반적으로 Non-Blocking Sync > Blocking Sync보다 효율적

왜냐면 Non-Blocking Sync은 호출 함수가 제어권을 갖고 있어 다른 작업을 병렬적으로 수행할 수 있기 때문이라고.👀

e.g. 게임 로딩 화면, 파일 다운로드 진행바(파일을 다운받으며 웹서핑이나 유튜브를 볼 수 있지만, 사용자가 원하는 최종 작업인 파일 다운로드 작업을 수행)

Blocking Async VS Blocking Sync

개념적 차이가 있을 뿐 성능적 차이가 없다.

보통 Blocking Async은 개발자가 Non-Blocking Async으로 처리하려다 실수하는 경우에 발생
의도치 않은 경우에 사용되며 안티 패턴(anti-pattern)이라 치부하기도 한다.


결론
두 개념의 의미 차이가 명확하게 한 줄로 정리할 수 있는 시간이었다. 막연하게 단어나 기술을 사용할 때 보다 마음이 편안해진다.

대규모 트래픽 대응과 같이 중요하게 기술을 선택해야 하는 시점에 '왜 비동기여야 하는가?'를 답변 가능하게 한 배움이었다고 생각한다. 컴퓨터 아키텍처를 깊게 이해하는데 있어 도움이 될 것 같다.


참고

profile
기록이 주는 즐거움

2개의 댓글

comment-user-thumbnail
2023년 10월 26일

개념적인 부분은 확실히 작은 서비스에서는 숟가락으로 먹든 손으로 먹든 별 티가 안나지만 트래픽이 커질수록 디테일이 중요한거같다는 생각이 드네요

1개의 답글