🔥알림🔥
① 테디노트 유튜브 -
구경하러 가기!
② LangChain 한국어 튜토리얼
바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs)
바로가기 🙌
④ RAG 비법노트 LangChain 강의오픈
바로가기 🙌
⑤ 서울대 PyTorch 딥러닝 강의
바로가기 🙌
파이썬 비동기(async)함수와 코루틴(coroutine) 흐름 이해하기
파이썬에서 async
함수는 비동기 프로그래밍 을 위한 기능입니다. 파이썬 함수에서 def
키워드로 정의할 때 앞에 async
가 붙습니다. 파이썬의 비동기 함수가 많이 사용될 때는 I/O 바운드 작업 이나 높은 대기 시간이 예상되는 작업을 처리 할 때 유용합니다.
예를 들면, 머신러닝의 학습이 오래걸릴 때 동기함수로 처리를 하게 된다면, 머신러닝 학습이 다 끝날 때까지 아무런 작업(task)을 할 수 없는 상태가 됩니다.
대부분의 네트워크 통신에서도 비동기 처리를 하고 있기 때문에, 이번에 비동기 프로그래밍에 대한 맛보기라도 해보시는 것을 추천 드립니다.
여기서 비동기(async)
란, 프로그램이 특정 작업을 기다리는 동안 다른 작업을 수행할 수 있도록 하는 것을 의미 합니다.
⭐️ 비동기(async) 함수의 주요 특징
-
비동기 함수 선언:
async def
를 사용하여 함수를 선언합니다. 이렇게 선언된 함수는 ‘코루틴(coroutine)’ 을 반환합니다. -
await
키워드:await
키워드는 코루틴의 실행을 일시 중지하고, 완료될 때까지 기다립니다.await
는async
함수 내에서만 사용할 수 있습니다. -
이벤트 루프: 비동기 작업을 관리하기 위해 이벤트 루프를 사용합니다. 이벤트 루프는 작업이 완료될 때까지 기다리고, 완료되면 결과를 반환합니다.
-
비동기 I/O: 파일 읽기/쓰기, 네트워크 요청 등 I/O 작업을 비동기적으로 처리할 수 있습니다.
⭐️ 코루틴(coroutine)
코루틴을 더 쉽게 이해하기 위해, 일상 생활의 예를 들어보겠습니다. 요리사가 주방에서 여러 요리를 동시에 해야 하는 상황을 예로 들어 보겠습니다. 팬에 스테이크를 굽고 있는 동안, 쌀도 지어야 하고, 샐러드도 준비해야 합니다. 이 상황에서 당신은 코루틴 과 같이 작동합니다.
-
여러 작업을 번갈아 수행 : 스테이크가 익는 동안 당신은 쌀을 지을 수 있고, 쌀이 끓는 동안 샐러드를 만들 수 있습니다. 코루틴도 이와 같이 하나의 작업이 끝나기를 기다리는 동안 다른 작업을 수행할 수 있습니다.
-
작업의 중단과 재개 : 요리를 할 때 한 요리에서 다른 요리로 전환할 때, 각 요리의 현재 상태를 기억해야 합니다. 마찬가지로 코루틴은 중단된 지점을 기억하고, 나중에 그 지점부터 작업을 계속합니다.
-
효율적인 작업 관리 : 한 요리가 자동으로 진행되는 동안 다른 요리를 준비함으로써 시간을 절약할 수 있습니다. 예를 들어, 스테이크가 팬에서 익는 동안 샐러드를 만들 수 있습니다. 코루틴은 이와 유사하게, 효율적으로 여러 작업을 동시에 관리할 수 있습니다.
파이썬에서 async
와 await
키워드를 사용해 코루틴을 만듭니다. async
로 정의된 함수는 ‘할 일 목록’ 과 같고, await
은 ‘이 할 일이 끝날 때까지 기다리는 것’ 과 비슷합니다. 이를 통해 여러 작업을 동시에 수행하면서도, 각 작업이 서로 방해받지 않도록 관리 할 수 있습니다.
간단히 말해서, 코루틴은 여러 일을 동시에 하면서도 각각의 일이 서로 방해받지 않도록 하는 스마트한 방법 입니다. 이를 통해 프로그램이 더 효율적으로 동작하게 합니다.
🔥 예제 코드
아래는 일반 루틴의 예시 입니다. task(1), task(2), task(3) 는 순차적으로 실행되며 앞선 task 가 종료될 때까지 뒤의 task 는 기다립니다.
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) 처리 함수를 활용한 예시를 보도록 하겠습니다.
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
프로그램의 흐름
-
함수 정의: task라는 비동기 함수를 정의합니다. 이 함수는 입력된 초(seconds)만큼 대기한 후, 작업 완료 메시지를 출력합니다.
-
대기(await asyncio.sleep(seconds)):
await
키워드는asyncio.sleep
함수의 완료를 기다립니다. 이 함수는 비동기적으로 실행되어, 지정된 시간(초) 동안 프로그램의 다른 부분을 차단하지 않습니다. -
여러 작업 동시 실행: main 함수 내에서
asyncio.gather
를 사용하여 task 함수를 세 번 호출합니다. 각 호출은 다른 시간(1초, 2초, 3초) 동안 대기합니다. gather는 이 세 작업을 거의 동시에 시작하고, 모두 완료될 때까지 기다립니다. -
프로그램 실행: 마지막으로,
asyncio.run(main())
을 통해 main 코루틴을 시작합니다. 이는 전체 프로그램의 실행을 시작하는 지점입니다. main이 완료될 때까지 이벤트 루프가 계속 실행됩니다.
이 코드는 동시에 여러 비동기 작업을 수행하는 방법을 보여주며, 각 작업이 서로의 실행을 방해하지 않으면서도 효율적으로 진행됩니다.
댓글남기기