변수의 기록

(os) 비동기(Asynchronous) 프로그래밍과 운영체제 관점의 이해 본문

CS지식/운영체제 (Operating System)

(os) 비동기(Asynchronous) 프로그래밍과 운영체제 관점의 이해

불광동 물주먹 2025. 4. 18. 17:16

📌 비동기(Asynchronous) 프로그래밍과 운영체제 관점의 이해

 

1. 비동기(Asynchronous)의 기본 개념

비동기(Asynchronous)란 작업의 수행 순서가 반드시 직렬적일 필요가 없는 처리 방식이다. 요청을 보낸 후 그 결과가 도착할 때까지 대기하지 않고, 다른 작업을 수행하거나 나중에 콜백/이벤트로 결과를 받아 처리한다.

즉, 비동기 프로그래밍은 독립적이다. 한 작업의 결과를 기다리지 않아도 다른 작업을 병렬로 진행할 수 있다.


2. 비동기의 다양한 쓰레드 모델 예시

아래는 비동기 프로그램이 어떻게 다양한 쓰레드 모델로 구현될 수 있는지를 설명한 사례다.

  • (1) 쓰레드 1개에 1작업 (기본 싱글 스레드 모델)
    • 구조: 하나의 쓰레드가 하나의 작업을 수행
    • 대표 사례: JavaScript, Node.js (이벤트 루프 기반)
    • 특징:
      • CPU-bound 작업엔 비효율적일 수 있지만
      • I/O 작업이 많은 환경에서는 논블로킹 I/O를 이용한 이벤트 기반 처리로 매우 효율적
    • 장점:
      • 컨텍스트 스위칭이 없음 → 오버헤드 최소화
    • 단점:
      • 병렬 처리 어려움, 오직 논블로킹 환경에서만 확장성 확보

    (2) 쓰레드 4개에 각 1작업 (전형적인 병렬 처리 구조)
    • 구조: 작업 수 = 쓰레드 수 → 1:1 매핑
    • 특징:
      • 각 작업이 블로킹이라도 상관 없음 (쓰레드 독립)
      • 멀티코어 CPU에서 병렬로 실행됨
    • 장점:
      • 직관적이고 안정적인 처리
    • 단점:
      • 많은 쓰레드를 생성할 경우 스케줄링 비용(컨텍스트 스위칭), 메모리 사용량 증가

      • 핵심 포인트:
        • 쓰레드 하나가 논블로킹 I/O 작업을 던져놓고 다음 동기 작업을 수행
        • 다른 쓰레드는 그와 별개로 병렬 작업을 수행
      • 장점:
        • 적은 쓰레드로도 높은 처리량 확보
        • 동기/비동기 병행이 유연하게 가능
      • 단점:
        • 구조 복잡성 증가 (콜백 지옥, 흐름 추적 어려움)

      (4) 쓰레드 1개에 2작업 (이벤트 루프 기반 처리)
      • 구조: 하나의 쓰레드가 여러 작업을 관리 (주로 I/O 중심)
      • 처리 방식:
        • 작업 요청 → I/O는 OS에 위임 → 제어권 반환
        • 완료 시점에 이벤트 루프가 해당 콜백을 처리
      • 예: Node.js, Netty
      • 장점:
        • 쓰레드 수 최소화 → 리소스 절약
      • 단점:
        • CPU-bound 작업이 끼어들면 전체 흐름 정체 가능
        • 병렬성이 아닌 동시성 기반 모델

    (3) 쓰레드 2개로 4작업 분할 (Sync + Async 혼합 모델)
    • 구조: 적은 수의 쓰레드여러 작업을 병렬 분할 처리
    • 주요 전략:
      • CPU 연산이 많은 작업은 블로킹(동기) 처리
      • I/O 중심 작업은 논블로킹(비동기) 처리
    • 예시:
      • Spring WebFlux (reactive async)
      • 일부 API는 RestTemplate 등 동기 방식으로 호출

 


3. 비동기를 가능하게 하는 두 가지 핵심 기술

비동기를 구현하기 위한 핵심은 다음 두 가지다:

(1) 멀티스레딩 (Multi-threading)

  • 프로세스 내에서 여러 스레드를 생성하여 동시에 여러 작업을 처리할 수 있음
  • CPU-bound 작업이나 I/O 대기 시간이 짧은 경우에 유리
  • 자원 소비 많고, 스레드 수가 많을수록 스케줄링 비용 증가

(2) 논블로킹 I/O (Non-blocking I/O)

  • I/O 요청 후 대기하지 않고 즉시 제어권을 반환
  • 실제 결과는 준비되었을 때 이벤트 루프가 알려줌
  • 적은 수의 쓰레드로도 많은 작업을 처리 가능 → 처리량(Throughput) 극대화

결론적으로, **"스레드를 적게 유지하면서 Non-blocking을 활용하는 구조가 전체 처리량을 높이는 핵심"**이다.
예: Reactor, Netty, Node.js


4. Synchronous I/O vs Asynchronous I/O

 

구분 Synchronous I/O Asynchronous I/O
처리 방식 I/O 완료까지 호출자가 대기 호출 즉시 반환, 나중에 알림 수신
자원 효율 낮음 (쓰레드 블로킹됨) 높음 (리액티브, 이벤트 기반)
적용 사례 간단한 REST API, 일괄 처리 고성능 서버, 스트리밍 서비스
알림 방식 직접 결과 리턴 콜백, Future, Promise, Event

예시로 비동기 I/O를 사용하는 시스템은 이벤트 큐 기반으로 동작하며, I/O가 완료되면 OS 커널이나 라이브러리가 인터럽트나 이벤트로 알림을 준다.


5. 비동기의 아키텍처 활용: MSA + 메시지 큐

Microservices Architecture (MSA)에서는 비동기 메시징 시스템이 자주 활용된다.

  • 예시: A 서버 → B 서버 → C 서버
    직접 통신하지 않고 **메시지 큐(Kafka, RabbitMQ 등)**에 메시지를 넣어 비동기적으로 전달
  • 장점:
    • 느슨한 결합
    • 장애 격리
    • 확장성 확보
  • 단점:
    • 응답 지연 가능
    • 트랜잭션 처리 복잡성 증가

 

 

사진 출처 - 유튜트 쉬운코드

 

 

하지만, 경우에 따라 동기 API 호출이 유리한 상황도 있다.


6. 언제 메시지 큐가 아니라 API 호출이 더 나은가?

메시지 큐 기반 비동기 처리보다, API 통신이 더 효율적인 경우가 있다:

  • A 서버가 B 서버의 특정 데이터를 필요로 할 때
  • 요청 → 응답 시간이 짧고 실시간성이 중요한 경우
  • 비즈니스 로직 상, 결과를 즉시 확인해야 하는 경우

즉, 모든 상황에서 비동기가 정답은 아니며, 필요에 따라 비동기와 동기의 적절한 혼합이 중요하다.