본문 바로가기
목차
CE

동기 처리 및 비동기 처리

by ds31x 2026. 4. 10.
728x90
반응형

동기 처리(Synchronous Processing)와 비동기 처리(Asynchronous Processing)

두 방식의 핵심 차이는 작업 완료를 기다리는 방법에 있음.

  • Synchronous (동기): 요청한 작업이 끝날 때까지 제어 흐름(control flow)이 block됨
  • Asynchronous (비동기): 작업을 등록해 두고 제어권을 즉시 반환받으며, 결과는 나중에 회수함

 

간단한 예로는 vscode의 blocking / non-blocking 모드 실행을 들 수 있음:

vscode 를 터미널에서 code로 실행시 non-blocking 모드로 동작하여 vscode가 열리고 터미널은 다음 입력을 받기 위한 prompt가 출력된다. 즉, vscode와 터미널은 비동기적으로 실행되며 vscode의 종료에 상관없이 터미널은 입력을 받을 수 있음.

만약 code --wait 로 실행했다면 blocking 모드로 동작하며 vscode가 종료되어야 터미널은 다음 입력을 받을 수 있는 상태가 된다.


동기 처리 (Synchronous Processing)

import time

def read_data_sync():
    print("데이터 읽기 시작")
    time.sleep(3)
    print("데이터 읽기 완료")
    return "sample data"

data = read_data_sync()
print("받은 데이터:", data)
print("다음 작업 수행")

실행 순서는 다음과 같음:

  1. read_data_sync() 호출 : 3초간 block
  2. 반환 후 print("받은 데이터:", data) 실행
  3. print("다음 작업 수행") 실행

대기 시간(waiting time)이 곧 정지 시간으로 작용함.

동기 방식은 흐름은 단순하지만 자원 활용 측면에서 비효율적일 수 있음.


비동기 처리 (Asynchronous Processing)

다음은 Python의 native coroutine의 예임.

import asyncio


# async def로 정의된 함수는 coroutine function임.
# 이 함수를 호출하면 함수 본문이 바로 실행되는 것이 아니라,
# coroutine object 가 만들어짐.
async def read_data_async():
    # 이 출력은 read_data_async()가 실제로 실행되기 시작했을 때 출력됨.
    # 단순히 read_data_async()를 호출했다고 바로 출력되는 것은 아님.
    print("[read_data_async] 데이터 읽기 작업 시작")

    # asyncio.sleep(3)은 실제 I/O 대기 상황을 흉내내기 위한 비동기 대기임.
    #
    # await를 만나면 read_data_async()는 여기서 3초 동안 일시 중단됨.
    # 중요한 점은 이 3초 동안 program 전체가 멈추는 것이 아니라는 점임.
    #
    # read_data_async()가 멈춰 있는 동안 event loop는
    # main() 같은 다른 coroutine을 실행할 수 있음.
    print("[read_data_async] 3초 동안 데이터가 준비되기를 기다림")
    await asyncio.sleep(3)

    # 3초가 지난 뒤 event loop가 read_data_async()를 다시 실행하면
    # 이 줄부터 이어서 수행됨.
    print("[read_data_async] 데이터 읽기 작업 완료")

    # coroutine의 return 값은 나중에 await task를 통해 받을 수 있음.
    return "sample data"


async def main():
    # read_data_async()를 호출하면 coroutine object가 만들어짐.
    # create_task()는 이 coroutine object를 event loop에 Task로 등록함.
    #
    # 즉, read_data_async()를 "실행 예약"함.
    #
    # 단, 이 줄에서 read_data_async()가 즉시 끝까지 실행되는 것은 아님.
    # 현재는 main() coroutine이 계속 실행 중이므로,
    # event loop가 다른 Task를 실행할 기회를 얻어야 read_data_async()도 실행됨.
    task = asyncio.create_task(read_data_async())

    # create_task()는 read_data_async()가 끝날 때까지 기다리지 않음.
    # 따라서 main()은 바로 다음 줄을 계속 실행함.
    print("[main] read_data_async()를 Task로 등록함")
    print("[main] main()은 데이터 읽기 완료를 기다리지 않고 다른 작업을 계속 수행함")

    # main()이 await를 만남.
    #
    # await asyncio.sleep(1)은 main() coroutine을 약 1초 동안 일시 중단함.
    #
    # - asyncio.sleep(1)은 sleep coroutine 객체를 반환함.
    # - await는 해당 sleep coroutine 객체를 event loop에서 실행되도록 하고,
    #   그 결과가 완료될 때까지 현재 coroutine인 main()을 일시 중단함.
    # - 이때 main()은 block되는 것이 아니라 event loop에 제어권을 넘김.
    # - event loop는 그동안 앞에서 등록한 read_data_async() Task 등
    #   실행 가능한 다른 Task를 실행할 수 있음.
    print("[main] main()도 1초 동안 잠시 대기하며 event loop에 제어권을 넘김")
    await asyncio.sleep(1)

    # 1초가 지나면 main()이 다시 실행됨.
    # 이 시점에 read_data_async()는 이미 실행을 시작했고,
    # 내부의 await asyncio.sleep(3)에서 대기 중일 가능성이 큼.
    print("[main] 1초가 지남")
    print("[main] 아직 데이터 읽기 작업이 끝나지 않았을 수 있으므로 다른 작업을 계속 수행함")

    # 이제 read_data_async()의 결과가 필요함.
    #
    # await task는 task가 끝날 때까지 main()을 일시 중단함.
    # task가 이미 끝났다면 바로 결과를 받고,
    # 아직 끝나지 않았다면 끝날 때까지 기다림.
    print("[main] 이제 데이터가 필요하므로 read_data_async() Task의 완료를 기다림")
    data = await task

    # await task의 결과는 read_data_async()의 return 값임.
    print("[main] Task가 완료되어 결과를 받음")
    print("[main] 받은 데이터:", data)


# asyncio.run()은 event loop를 만들고,
# main() coroutine을 실행한 뒤,
# main()이 끝나면 event loop를 정리함.
asyncio.run(main())

실행 흐름은 다음과 같이 분리(decoupled)됨.

  1. read_data_async() 로 반환된 coroutine 객체 를 Task로 등록: create_task()
  2. Task로 등록된 coroutine 객체는 event loop에서 실행될 수 있도록 스케쥴링됨.
  3. main() (현재 실행 중인 coroutine객체)은 Task등록이 되자마자 다음 문장을 계속 실행함.
  4. main()await asyncio.sleep(1)를 만나 제어권을 event loop에 넘기면, event loop가 스케쥴링된 Task를 실행
    (1초 타임아웃 후 다시 awiat asyncio.sleep(1)을 호출한 main() 코루틴이 실행됨.)
  5. main()은 필요한 시점에 await task를 통해,  event loop에게 제어권을 넘기고, event loop가 해당 Task를 실행시킴.
  6. main()은 해당 Task의 완료를 기다리고 Task의 return 값을 회수하게 됨.

즉, 작업 시작 시점결과 사용 시점이 분리된다는 것이 핵심임.

 

위 예제의 동작을 이해하려면 asyncio에서 사용되는 다음의 주요 객체와 실행 흐름의 개념을 먼저 구분해야 함:

 

  • Coroutine 객체: async def 함수 가 호출될 때 만들어지는 객체로, 아직 실행되지 않은 비동기 작업의 본문을 담고 있음.
  • Task 객체: coroutine 객체event loop에 등록하여 실제 실행 대상으로 만든 객체로, 실행 상태와 결과를 관리함.
  • Event loop: 여러 Task를 관리하면서, 각 coroutine이 await로 멈춘 동안 다른 실행 가능한 Task를 번갈아 실행하는 실행 관리자임.
  • Sleep Coroutine 객체: asyncio.sleep(1)이 반환하는 coroutine 객체로, 지정한 시간 동안 현재 coroutine을 일시 중단하고 event loop가 다른 Task를 실행할 수 있게 함.

routine에 대한 보다 자세한 이해는 다음을 참고:

2026.06.11 - [CE] - [CE] Routine 이란

 

[CE] Routine 이란

프로그래밍 및 CS에서 routine은특정 작업을 수행하기 위해정해진 순서로 작성된명령어들의 묶음을 의미함.즉, 넓은 의미에서는 다음을 모두 routine이라고 부를 수 있음.routine ├─ function ├─ proce

ds31x.tistory.com

 


비교 요약

항목 Synchronous (동기) Asynchronous (비동기)
제어 흐름 block 즉시 반환
대기 시간 활용 불가 다른 작업 스케줄링 가능
코드 복잡도 낮음 상대적으로 높음
적합한 상황 단순 순차 작업 I/O-bound 작업

비동기 처리가 유리한 상황

CPU가 연산하는 것이 아니라, 응답을 기다리는 시간이 지배적인 작업에서 효과적임.

  • Network request (네트워크 요청)
  • File I/O (파일 입출력)
  • Database query (데이터베이스 조회)
  • 외부 API 호출

일반적으로 I/O-bound 작업에서 유용함:

  • Asynchronous 에서는
  • 대기 시간 동안 event loop가 다른 coroutine(코루틴)을 스케줄링할 수 있음.
  • 이를 통해, 전체 처리량(throughput)이 크게 향상됨.

같이 보면 좋은 자료들

2026.04.13 - [Python/PySide PyQt] - Qt에서 Event와 Event Handling - Event Loop

 

Qt에서 Event와 Event Handling - Event Loop

Qt에서 Event와 Event HandlingEvent란? GUI application에서 event란 사용자 또는 시스템에서 발생하는 모든 종류의 "사건"을 의미함.사용자가 마우스를 클릭함키보드 키를 누름창이 다른 창에 가려졌다가 다

ds31x.tistory.com

2023.06.20 - [utils/git and github] - [Git] git 에서 editor 로 VS Code 사용하기.

 

[Git] git 에서 editor 로 VS Code 사용하기.

넋두리Linux환경에서 가장 적응하기 귀찮은 게 무엇이냐라고 묻는다면...editor라고 말하고 싶다. 특히, 터치(?) 세대들은 gui에 익숙하다 보니 cli도 굉장히 부담스러워한다. vim은 꼭 익히길 권하지

ds31x.tistory.com

2025.06.07 - [Python] - [Py] subprocess 모듈 사용법.

 

[Py] subprocess 모듈 사용법.

이 문서는 subprocess Module 의 HighLevel Methods의 사용법을 다룸:subprocess.run(),subprocess.getoutput(),subprocess.check_output(),subprocess.PopenPython 3.5 이상을 기준으로 작성됨.1. subprocess 모듈이란?subprocess 모듈은 Pytho

ds31x.tistory.com


 

728x90

'CE' 카테고리의 다른 글

OS Platform and CPU Architecture  (0) 2026.05.24
TOML 파일: 설정파일 형식  (0) 2026.04.10
CE: Independent vs Agnostic  (0) 2026.03.17
Machine 이란?  (0) 2026.03.03
Modulation 의 여러 정의  (0) 2026.03.02