🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs) 바로가기 🙌

4 분 소요

파이썬에서 async 함수는 비동기 프로그래밍 을 위한 기능입니다. 파이썬 함수에서 def 키워드로 정의할 때 앞에 async 가 붙습니다. 파이썬의 비동기 함수가 많이 사용될 때는 I/O 바운드 작업 이나 높은 대기 시간이 예상되는 작업을 처리 할 때 유용합니다.


예를 들면, 머신러닝의 학습이 오래걸릴 때 동기함수로 처리를 하게 된다면, 머신러닝 학습이 다 끝날 때까지 아무런 작업(task)을 할 수 없는 상태가 됩니다.


대부분의 네트워크 통신에서도 비동기 처리를 하고 있기 때문에, 이번에 비동기 프로그래밍에 대한 맛보기라도 해보시는 것을 추천 드립니다.


여기서 비동기(async) 란, 프로그램이 특정 작업을 기다리는 동안 다른 작업을 수행할 수 있도록 하는 것을 의미 합니다.

⭐️ 비동기(async) 함수의 주요 특징

  • 비동기 함수 선언: async def 를 사용하여 함수를 선언합니다. 이렇게 선언된 함수는 ‘코루틴(coroutine)’ 을 반환합니다.

  • await 키워드: await 키워드는 코루틴의 실행을 일시 중지하고, 완료될 때까지 기다립니다. awaitasync 함수 내에서만 사용할 수 있습니다.

  • 이벤트 루프: 비동기 작업을 관리하기 위해 이벤트 루프를 사용합니다. 이벤트 루프는 작업이 완료될 때까지 기다리고, 완료되면 결과를 반환합니다.

  • 비동기 I/O: 파일 읽기/쓰기, 네트워크 요청 등 I/O 작업을 비동기적으로 처리할 수 있습니다.

⭐️ 코루틴(coroutine)

코루틴을 더 쉽게 이해하기 위해, 일상 생활의 예를 들어보겠습니다. 요리사가 주방에서 여러 요리를 동시에 해야 하는 상황을 예로 들어 보겠습니다. 팬에 스테이크를 굽고 있는 동안, 쌀도 지어야 하고, 샐러드도 준비해야 합니다. 이 상황에서 당신은 코루틴 과 같이 작동합니다.

  • 여러 작업을 번갈아 수행 : 스테이크가 익는 동안 당신은 쌀을 지을 수 있고, 쌀이 끓는 동안 샐러드를 만들 수 있습니다. 코루틴도 이와 같이 하나의 작업이 끝나기를 기다리는 동안 다른 작업을 수행할 수 있습니다.

  • 작업의 중단과 재개 : 요리를 할 때 한 요리에서 다른 요리로 전환할 때, 각 요리의 현재 상태를 기억해야 합니다. 마찬가지로 코루틴은 중단된 지점을 기억하고, 나중에 그 지점부터 작업을 계속합니다.

  • 효율적인 작업 관리 : 한 요리가 자동으로 진행되는 동안 다른 요리를 준비함으로써 시간을 절약할 수 있습니다. 예를 들어, 스테이크가 팬에서 익는 동안 샐러드를 만들 수 있습니다. 코루틴은 이와 유사하게, 효율적으로 여러 작업을 동시에 관리할 수 있습니다.

파이썬에서 asyncawait 키워드를 사용해 코루틴을 만듭니다. async 로 정의된 함수는 ‘할 일 목록’ 과 같고, await‘이 할 일이 끝날 때까지 기다리는 것’ 과 비슷합니다. 이를 통해 여러 작업을 동시에 수행하면서도, 각 작업이 서로 방해받지 않도록 관리 할 수 있습니다.

간단히 말해서, 코루틴은 여러 일을 동시에 하면서도 각각의 일이 서로 방해받지 않도록 하는 스마트한 방법 입니다. 이를 통해 프로그램이 더 효율적으로 동작하게 합니다.

🔥 예제 코드

아래는 일반 루틴의 예시 입니다. task(1), task(2), task(3) 는 순차적으로 실행되며 앞선 task 가 종료될 때까지 뒤의 task 는 기다립니다.

normal-routine

import time
import datetime

def task(seconds):
    print(f'[작업시작] {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
    # 시작 메시지를 출력합니다. 작업이 몇 초 후에 끝날지 알려줍니다.
    print(f"이 작업은 {seconds} 초 뒤 종료됩니다.")
    
    # time.sleep 함수를 사용하여 지정된 시간 동안 대기합니다.
    time.sleep(seconds)
    
    # 대기 시간이 끝나면, 작업 완료 메시지를 출력합니다.
    print(f'[작업종료] {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
    print(f"작업이 끝났습니다.")

def main():
    # task(1), task(2), task(3)을 순차적으로 호출합니다.
    task(1)
    task(2)
    task(3)
    
main()
[작업시작] 2023-12-05 23:56:16
이 작업은 1 초 뒤 종료됩니다.
[작업종료] 2023-12-05 23:56:17
작업이 끝났습니다.
[작업시작] 2023-12-05 23:56:17
이 작업은 2 초 뒤 종료됩니다.
[작업종료] 2023-12-05 23:56:19
작업이 끝났습니다.
[작업시작] 2023-12-05 23:56:19
이 작업은 3 초 뒤 종료됩니다.
[작업종료] 2023-12-05 23:56:22
작업이 끝났습니다.

아래는 이전에 동기처리의 함수대신 비동기(async) 처리 함수를 활용한 예시를 보도록 하겠습니다.

coroutine

import asyncio  # asyncio 라이브러리를 가져옵니다. 비동기 프로그래밍을 위한 파이썬 표준 라이브러리입니다.
import datetime

# 비동기적으로 실행될 함수를 정의합니다. 'async def'를 사용하여 정의합니다.
async def task(seconds):
    print(f'[작업시작] {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')
    # 시작 메시지를 출력합니다. 작업이 몇 초 후에 끝날지 알려줍니다.
    print(f"이 작업은 {seconds} 초 뒤 종료됩니다.")
    
    # asyncio.sleep 함수를 사용하여 비동기적으로 지정된 시간 동안 대기합니다.
    # 'await'는 이 함수가 완료될 때까지 현재 코루틴의 실행을 일시 중지합니다.
    await asyncio.sleep(seconds)
    
    # 대기 시간이 끝나면, 작업 완료 메시지를 출력합니다.
    print(f"작업이 끝났습니다.")
    print(f'[작업종료] {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}')

# 메인 함수를 정의합니다. 이 함수도 비동기적으로 실행됩니다.
async def main():
    # asyncio.gather를 사용하여 여러 코루틴(task 함수 호출)을 동시에 실행합니다.
    # 이렇게 하면 task(1), task(2), task(3)이 거의 동시에 시작됩니다.
    await asyncio.gather(
        task(1),
        task(2),
        task(3)
    )

# asyncio.run을 사용하여 메인 함수를 실행합니다. 이는 프로그램의 시작점입니다.
# 이벤트 루프를 시작하고 main 코루틴을 실행합니다.
asyncio.run(main())


# --- 출력 ---
# [작업시작] 2023-12-05 23:41:04
# 이 작업은 1 초 뒤 종료됩니다.
# [작업시작] 2023-12-05 23:41:04
# 이 작업은 2 초 뒤 종료됩니다.
# [작업시작] 2023-12-05 23:41:04
# 이 작업은 3 초 뒤 종료됩니다.
# 작업이 끝났습니다.
# [작업종료] 2023-12-05 23:41:05
# 작업이 끝났습니다.
# [작업종료] 2023-12-05 23:41:06
# 작업이 끝났습니다.
# [작업종료] 2023-12-05 23:41:07

프로그램의 흐름

  1. 함수 정의: task라는 비동기 함수를 정의합니다. 이 함수는 입력된 초(seconds)만큼 대기한 후, 작업 완료 메시지를 출력합니다.

  2. 대기(await asyncio.sleep(seconds)): await 키워드는 asyncio.sleep 함수의 완료를 기다립니다. 이 함수는 비동기적으로 실행되어, 지정된 시간(초) 동안 프로그램의 다른 부분을 차단하지 않습니다.

  3. 여러 작업 동시 실행: main 함수 내에서 asyncio.gather 를 사용하여 task 함수를 세 번 호출합니다. 각 호출은 다른 시간(1초, 2초, 3초) 동안 대기합니다. gather는 이 세 작업을 거의 동시에 시작하고, 모두 완료될 때까지 기다립니다.

  4. 프로그램 실행: 마지막으로, asyncio.run(main()) 을 통해 main 코루틴을 시작합니다. 이는 전체 프로그램의 실행을 시작하는 지점입니다. main이 완료될 때까지 이벤트 루프가 계속 실행됩니다.

이 코드는 동시에 여러 비동기 작업을 수행하는 방법을 보여주며, 각 작업이 서로의 실행을 방해하지 않으면서도 효율적으로 진행됩니다.

댓글남기기