파이썬의 비동기 프로그래밍을 다루는 asyncio는 현대 프로그래밍에서 매우 중요한 개념입니다. 특히, 비동기 I/O를 통해 효율적인 코드 작성을 가능하게 해줍니다. 이번 포스트에서는 asyncio의 기본 개념부터 시작하여, asyncio.run()의 동작 원리와 비동기 코드를 작성하는 방법에 대해 자세히 알아보겠습니다.
(python 3.11 기준으로 작성되었습니다.)
asyncio란 무엇인가?
asyncio는 파이썬에서 비동기 프로그래밍을 지원하는 라이브러리입니다. 이 라이브러리는 async와 await 구문을 사용하여 동시성 코드를 작성할 수 있게 해줍니다. 비동기 프로그래밍은 여러 작업을 동시에 수행할 수 있도록 도와주며, 특히 I/O 작업에서 성능을 크게 향상시킬 수 있습니다.
asyncio.run()의 기본 개념
asyncio.run()은 비동기 코드를 실행하기 위한 진입점입니다. 이 함수는 새로운 이벤트 루프를 생성하고, 주어진 코루틴을 실행합니다. 이 과정에서 이벤트 루프는 비동기 작업을 관리하고, 완료된 작업의 결과를 반환합니다.
비동기 코드 작성하기
비동기 코드를 작성하기 위해서는 async 키워드를 사용하여 함수를 정의하고, await 키워드를 사용하여 다른 비동기 작업을 호출해야 합니다. 예를 들어, 다음과 같은 간단한 비동기 함수를 작성할 수 있습니다.
python import asyncio
async def main(): print("Hello, World!")
asyncio.run(main())
위 코드를 실행하면 "Hello, World!"가 출력됩니다. 이처럼 asyncio.run()을 통해 비동기 함수를 실행할 수 있습니다.
event loop의 동작 원리
실제로 내부를 보면 event loop을 제어하는 방식을 사용하고 있습니다. 이벤트 루프는 비동기 작업을 관리하며, 각 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행할 수 있도록 합니다. 이벤트 루프는 작업 큐에서 작업을 가져와 실행하고, 만약 어떤 작업이 다른 작업을 기다리고 있다면, 그 작업은 일시 중지되고 다른 작업이 실행됩니다. 이 과정은 비동기 작업이 효율적으로 수행될 수 있도록 도와줍니다.
# Simplified version of asyncio.run() from the standard library
import asyncio
def run(main):
"""
Executes the coroutine and returns its result.
Creates a new event loop, runs the given coroutine until complete, and
then closes the loop.
"""
if asyncio.get_running_loop() is not None:
raise RuntimeError("asyncio.run() cannot be called from a running event loop")
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
return loop.run_until_complete(main)
finally:
try:
loop.run_until_complete(loop.shutdown_asyncgens())
finally:
asyncio.set_event_loop(None)
loop.close()
- 이벤트 루프 실행 여부 확인: asyncio.run()은 현재 스레드에 이미 실행 중인 이벤트 루프가 있는지 확인합니다. 만약 실행 중인 루프가 있다면, RuntimeError 예외를 발생시켜 중첩 실행을 방지합니다.
- 새로운 이벤트 루프 생성 및 설정: 기존 루프가 없으면, 새로운 이벤트 루프를 생성(asyncio.new_event_loop())하고 이를 현재 이벤트 루프로 설정합니다(asyncio.set_event_loop()).
- 코루틴 실행: 전달된 코루틴을 loop.run_until_complete()로 실행하여 완료될 때까지 기다립니다. 이 단계에서 코루틴이 끝날 때까지 블로킹됩니다.
- 루프 종료 및 정리: 코루틴이 완료되면, loop.shutdown_asyncgens()를 통해 비동기 생성기를 정리하고, 현재 이벤트 루프를 None으로 설정한 뒤(asyncio.set_event_loop(None)) 루프를 닫습니다(loop.close()).
이 구조를 통해 각 asyncio.run() 호출은 독립된 이벤트 루프에서 실행되며, 이벤트 루프의 생성과 종료가 자동으로 관리되어 코드 사용이 간단해집니다.
비동기 I/O와 동기 I/O의 차이
비동기 I/O와 동기 I/O의 차이는 작업이 완료될 때까지 기다리는 방식에 있습니다. 동기 I/O는 각 작업이 완료될 때까지 다음 작업을 기다리지만, 비동기 I/O는 여러 작업을 동시에 진행할 수 있습니다. 이로 인해 비동기 I/O는 자원을 더 효율적으로 사용할 수 있습니다.
실제 코드 예제
다음은 비동기 작업을 여러 개 동시에 실행하는 예제입니다.
import asyncio
async def task(name, delay):
print(f"{name} 시작...")
await asyncio.sleep(delay)
print(f"{name} 종료 (대기 시간: {delay}초)")
async def main():
await asyncio.gather(
task("작업 1", 1),
task("작업 2", 2),
task("작업 3", 3),
)
asyncio.run(main())
위 코드를 실행하면 각 작업이 비동기적으로 실행되며, 작업이 완료되는 순서에 따라 출력됩니다.
비동기 프로그래밍의 장점
비동기 프로그래밍의 가장 큰 장점은 성능입니다. 특히, 네트워크 요청이나 파일 I/O와 같은 작업에서 비동기 프로그래밍을 사용하면, 프로그램의 응답성을 높이고 자원을 효율적으로 사용할 수 있습니다. 또한, 비동기 프로그래밍은 코드의 가독성을 높이고, 복잡한 비동기 작업을 쉽게 관리할 수 있도록 도와줍니다.
마무리 및 추가 자료
비동기 프로그래밍은 현대 소프트웨어 개발에서 필수적인 기술입니다. asyncio를 활용하면 비동기 작업을 쉽게 관리할 수 있으며, 성능을 극대화할 수 있습니다. 더 자세한 내용은 Python Docs의 asyncio 문서에서 확인할 수 있습니다.
비동기 프로그래밍에 대한 이해를 높이기 위해 다양한 예제를 실습해보는 것을 추천합니다.
태그: #Python #asyncio #비동기프로그래밍 #코루틴 #이벤트루프
이런 자료를 참고 했어요.
[1] Python Docs - asyncio — 비동기 I/O — Python 3.11.10 문서 (https://docs.python.org/ko/3.11/library/asyncio.html)
[2] Python Docs - asyncio로 개발하기 — Python 3.11.10 문서 (https://docs.python.org/ko/3.11/library/asyncio-dev.html)
[3] 티스토리 - [Python]파이썬 비동기 프로그래밍 동작 원리에 대해서 (feat ... (https://leffept.tistory.com/512)
[4] velog - 파이썬 asyncio 코루틴과 태스크 (https://velog.io/@stresszero/python-asyncio-ct)