본문으로 건너뛰기
Minsing Log
뒤로 가기

비동기 처리의 작업 과정 원리

Programming & CS

비동기 처리 원리

비동기 처리의 핵심은 동시성을 제공하면서도 단일 스레드에서 실행된다는 점입니다. 이는 주로 I/O 작업(예: 파일 읽기/쓰기, 네트워크 요청 등)에서 유용합니다. 비동기 처리는 이러한 작업을 기다리는 동안 CPU가 다른 작업을 처리할 수 있도록 해줍니다.

주요 개념

  1. 이벤트 루프 (Event Loop):

    • 비동기 처리의 중심입니다. 이벤트 루프는 여러 작업을 관리하고, 각 작업이 완료될 때까지 기다렸다가 다시 실행을 재개합니다.
    • 이벤트 루프는 비동기 함수(코루틴)를 스케줄링하고, I/O 작업이 완료되면 해당 작업을 다시 실행합니다.
  2. 코루틴 (Coroutines):

    • async def로 정의된 함수입니다. 코루틴은 실행 중에 await 키워드를 만나면 실행을 일시 중단하고, 다른 작업이 실행될 수 있도록 제어를 이벤트 루프에 넘깁니다.
    • 코루틴은 비동기 함수로, 동기 함수와 달리 실행을 중단하고 나중에 다시 재개할 수 있습니다.
  3. Futures:

    • 아직 완료되지 않은 작업의 결과를 나타내는 객체입니다. Future 객체는 나중에 완료될 작업의 결과를 포함하며, 이벤트 루프가 해당 작업이 완료되면 결과를 반환합니다.
  4. Tasks:

    • 코루틴을 실행하기 위해 이벤트 루프에 의해 관리되는 객체입니다. asyncio.Task는 코루틴을 감싸서 실행을 관리하며, 완료되면 Future 객체로 결과를 반환합니다.

비동기 처리의 동작 원리

  1. 이벤트 루프 시작:

    • asyncio.run() 또는 asyncio.get_event_loop()를 호출하여 이벤트 루프를 시작합니다.
    • 이벤트 루프는 대기 중인 작업(코루틴)을 관리하고, 각 작업이 완료될 때까지 기다립니다.
  2. 코루틴 실행:

    • 코루틴은 await 키워드를 만나면 실행을 중단하고, 이벤트 루프는 다른 작업을 실행합니다.
    • 예를 들어, 네트워크 요청을 보내고 응답을 기다리는 동안 다른 코루틴이 실행될 수 있습니다.
  3. I/O 작업 대기:

    • 코루틴이 await를 통해 I/O 작업을 기다리는 동안, 이벤트 루프는 다른 코루틴을 실행합니다. 이로 인해 CPU가 유휴 상태로 남지 않고 다른 작업을 처리할 수 있습니다.
  4. 작업 완료:

    • I/O 작업이 완료되면 이벤트 루프는 해당 코루틴을 다시 실행하여 남은 작업을 처리합니다.

예시 코드

import asyncio

async def fetch_data():
    print("데이터 요청 중...")
    await asyncio.sleep(2)  # 네트워크 요청을 시뮬레이션
    print("데이터 수신 완료")
    return "데이터"

async def main():
    print("비동기 작업 시작")
    result = await fetch_data()
    print(f"결과: {result}")

# 이벤트 루프 실행
asyncio.run(main())

코드 설명

  1. fetch_data 코루틴:

    • async def로 정의된 비동기 함수입니다. await asyncio.sleep(2)는 2초 동안 대기하는 동안 다른 작업이 실행될 수 있도록 합니다.
  2. main 코루틴:

    • fetch_data 코루틴을 호출하고, 그 결과를 기다립니다. await 키워드를 사용하여 fetch_data가 완료될 때까지 기다립니다.
  3. 이벤트 루프 실행:

    • asyncio.run(main())을 통해 이벤트 루프를 시작하고, main 코루틴을 실행합니다.

비동기 처리의 장점

결론

비동기 처리는 이벤트 루프와 코루틴을 사용하여 I/O 작업을 효율적으로 처리하는 방식입니다. 이를 통해 단일 스레드에서 여러 작업을 동시에 처리할 수 있으며, 특히 I/O 바운드 작업에서 성능을 크게 향상시킬 수 있습니다.


비동기 처리의 핵심은 동시성을 제공하는 것이지만, 이는 병렬 처리와는 다르다는 점을 기억해야 해. 이제 질문에 대한 답을 단계별로 설명할게.

비동기 작업의 흐름

  1. 작업 시작과 일시 중단:

    • 비동기 함수(코루틴)는 await 키워드를 만나면 실행을 일시 중단해. 이때, 해당 작업은 완전히 멈추는 것이 아니라, 다른 작업이 실행될 수 있도록 제어권을 이벤트 루프에 넘기는 것이야.
    • 예를 들어, 네트워크 요청을 보내고 응답을 기다리는 동안, CPU는 다른 작업을 처리할 수 있어. 이때, 첫 번째 작업은 네트워크 응답을 기다리며 멈춰있지만, 이벤트 루프는 다른 작업을 실행할 수 있는 상태가 돼.
  2. 다음 작업 시작:

    • 첫 번째 작업이 await로 인해 일시 중단된 동안, 이벤트 루프는 다른 비동기 작업을 실행해. 이 작업도 마찬가지로 await를 만나면 일시 중단될 수 있어.
    • 중요한 점은 첫 번째 작업이 멈춰있는 동안에도 이벤트 루프는 다른 작업을 계속해서 실행한다는 거야. 즉, 첫 번째 작업이 멈춘 상태에서 두 번째 작업이 시작되고, 두 번째 작업이 await를 만나면 다시 첫 번째 작업으로 돌아갈 수 있어.
  3. 작업이 동시에 유지되는가?:

    • 첫 번째 작업이 멈춘 상태에서 두 번째 작업이 시작되지만, 첫 번째 작업은 여전히 이벤트 루프에 의해 관리되고 있어. 즉, 첫 번째 작업이 완전히 종료된 것이 아니라, 일시 중단된 상태로 유지되고 있는 거야.
    • 두 번째 작업이 실행되는 동안, 첫 번째 작업은 백그라운드에서 대기하고 있다가, 다시 실행될 기회가 오면 재개돼.

비유를 통한 설명

비동기 처리를 이해하기 쉽게 비유해볼게:

이 과정에서 두 작업이 동시에 실행되는 것처럼 보이지만, 실제로는 이벤트 루프가 작업을 번갈아가며 처리하는 거야. 즉, 첫 번째 작업이 멈춘 동안에도 두 번째 작업이 실행되고, 첫 번째 작업은 백그라운드에서 유지되다가 다시 실행될 기회를 얻는 거지.

코드 예시

import asyncio

async def task1():
    print("Task 1 시작")
    await asyncio.sleep(2)  # 2초 동안 대기 (네트워크 요청 대기 시뮬레이션)
    print("Task 1 완료")

async def task2():
    print("Task 2 시작")
    await asyncio.sleep(1)  # 1초 동안 대기 (파일 읽기 시뮬레이션)
    print("Task 2 완료")

async def main():
    # 두 작업을 동시에 실행
    await asyncio.gather(task1(), task2())

# 이벤트 루프 실행
asyncio.run(main())

코드 설명

  1. task1:

    • 2초 동안 대기하는 작업이야. 이 작업은 await asyncio.sleep(2)로 인해 2초 동안 멈추고, 그동안 다른 작업이 실행될 수 있어.
  2. task2:

    • 1초 동안 대기하는 작업이야. await asyncio.sleep(1)로 인해 1초 동안 멈추고, 그동안 다른 작업이 실행될 수 있어.
  3. asyncio.gather:

    • 두 작업을 동시에 실행해. task1이 2초 동안 대기하는 동안, task2는 1초 동안 대기하고, task2가 끝나면 다시 task1이 재개돼.

결론

공개 여정 구독

글이 올라오면 이메일로 보내드릴게요.

개발, 창업, 배움, 실패와 회고를 짧고 솔직하게 기록합니다. 검색으로 스쳐가는 독자가 아니라 여정을 따라오는 팬을 만들기 위한 뉴스레터입니다.

뉴스레터는 준비 중입니다. 그동안 RSS 로 구독하거나 이메일 로 알림을 요청해 주세요.


이 글 공유하기:

댓글

GitHub 계정으로 로그인해 댓글을 남길 수 있습니다.


이전 글
Async에서 결과가 순서대로 안나옴
다음 글
OSError: [Errno 8] Exec format error: '/Users/naya/.wdm/drivers/chromedriver/mac64/127.0.6533.88/chromedriver-mac-arm64/THIRD_PARTY_NOTICES.chromedriver'\n