asyncio는 파이썬의 비동기 프로그래밍을 위한 표준 라이브러리로, 비동기 입출력(I/O) 작업을 효율적으로 처리하고 동시성을 관리할 수 있게 해줍니다. asyncio는 특히 파일 입출력, 네트워크 요청, 데이터베이스 쿼리 같은 I/O 바운드 작업에 적합하며, CPU를 차지하지 않고 여러 작업을 동시에 수행할 수 있도록 돕습니다.
아래에서 asyncio의 기본 개념, 주요 구성 요소, 예제, 그리고 비동기 프로그래밍의 장점에 대해 살펴보겠습니다.
1. asyncio의 개념
asyncio는 이벤트 루프(Event Loop)를 중심으로 비동기 작업을 관리합니다. 이벤트 루프는 코루틴을 실행하고 중단 지점(예: await)에서 다른 작업으로 전환하여 여러 작업이 마치 동시에 실행되는 것처럼 동작하도록 합니다.
주요 구성 요소
• 코루틴(Coroutine): async def로 정의된 비동기 함수로, await 키워드를 통해 실행이 중단되었다가 재개됩니다.
• 이벤트 루프(Event Loop): 모든 비동기 작업을 관리하는 중앙 컨트롤러로, 각 코루틴의 실행 상태를 조정하며, 완료된 작업을 종료하고 새로운 작업을 실행합니다.
• 태스크(Task): 코루틴을 감싸고 이벤트 루프에서 실행될 수 있도록 하는 객체입니다.
• 퓨처(Future): 아직 완료되지 않은 작업을 나타내는 객체로, 결과가 미래에 결정되는 객체입니다. await을 통해 해당 작업의 결과를 기다릴 수 있습니다.
2. asyncio의 주요 기능
asyncio.run()
asyncio.run()은 주어진 코루틴을 이벤트 루프에서 실행하고 완료될 때까지 대기하는 함수입니다. 일반적으로 가장 상위 레벨의 비동기 작업을 실행할 때 사용합니다.
import asyncio
async def main():
print("Hello")
await asyncio.sleep(1)
print("World")
# main 코루틴을 실행
asyncio.run(main())
asyncio.sleep()
asyncio.sleep()은 비동기적으로 시간을 지연시키는 함수입니다. 다른 sleep 함수와 달리, 이 함수는 메인 스레드를 차단하지 않고, 그동안 다른 코루틴을 실행할 수 있습니다.
async def delayed_message():
print("Start waiting...")
await asyncio.sleep(2) # 2초 지연
print("2 seconds later...")
asyncio.gather()
asyncio.gather()는 여러 코루틴을 동시에 실행하기 위해 사용됩니다. 리스트나 튜플로 여러 코루틴을 전달하면, 모든 코루틴이 완료될 때까지 대기합니다.
async def task(name, delay):
print(f"{name} started")
await asyncio.sleep(delay)
print(f"{name} finished")
async def main():
await asyncio.gather(
task("Task 1", 2),
task("Task 2", 1)
)
asyncio.run(main())
asyncio.create_task()
asyncio.create_task()는 이벤트 루프에서 코루틴을 바로 실행하기 위해 태스크 객체를 생성합니다. 생성된 태스크는 백그라운드에서 실행되므로, 동시에 여러 작업을 실행할 때 유용합니다.
async def task(name):
print(f"{name} started")
await asyncio.sleep(1)
print(f"{name} finished")
async def main():
# 백그라운드에서 코루틴 실행
task1 = asyncio.create_task(task("Task 1"))
task2 = asyncio.create_task(task("Task 2"))
# 모든 태스크가 끝날 때까지 대기
await task1
await task2
asyncio.run(main())
3. 예제: 웹 스크래핑 작업에 asyncio 적용하기
여러 웹 페이지를 비동기적으로 스크래핑하는 예제입니다.
import asyncio
import aiohttp # 비동기 HTTP 클라이언트
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://example.com",
"https://example.org",
"https://example.net"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(f"Fetched {len(result)} characters")
asyncio.run(main())
위 예제에서는 aiohttp를 사용하여 비동기 HTTP 요청을 보냅니다. asyncio.gather를 통해 여러 URL을 동시에 요청하고, 응답 결과를 results에 저장한 뒤 출력합니다. 이렇게 하면 웹 페이지를 순차적으로 요청할 때보다 훨씬 빠르게 데이터를 가져올 수 있습니다.
4. asyncio를 사용할 때의 장점
• 효율적인 자원 사용: asyncio는 CPU 시간을 최적화하여 비동기 작업을 효율적으로 처리합니다. 비동기 작업 중에 다른 코루틴을 실행할 수 있어 리소스를 절약합니다.
• 높은 처리량: 여러 비동기 작업을 동시에 실행하여 응답 속도를 높이고, 처리량을 극대화할 수 있습니다.
• 코드 가독성 향상: await 키워드를 통해 비동기 작업을 순차적인 코드처럼 작성할 수 있어 코드가 직관적입니다.
마무리
asyncio는 파이썬의 강력한 비동기 프로그래밍 도구로, I/O 바운드 작업에서 특히 효과적입니다. 이벤트 루프, 코루틴, 태스크 등의 개념을 이해하고 다양한 비동기 함수를 활용하면 비동기 프로그램을 효율적으로 작성할 수 있습니다.