🖥️ IT, 컴퓨터/🐍 Python

[Python] 파이썬 비동기 프로그래밍이란, asyncio 라이브러리

김 홍시 2024. 3. 5.
반응형

파이썬의 비동기 프로그래밍과 asyncio는 I/O 바운드 작업(예: 네트워크 요청, 파일 읽기/쓰기)을 효율적으로 처리하기 위해 제공되는 기능입니다. 이 기능은 CPU를 기다리는 작업에서 유휴 상태로 두는 대신 다른 작업을 실행하도록 설계되어 있습니다. 아래에서 asyncio와 관련된 핵심 개념, 동작 원리 및 사용 방법을 자세히 설명하겠습니다.


1. 비동기 프로그래밍의 기본 개념

비동기 프로그래밍은 동시에 여러 작업을 처리하는 방법입니다. 그러나 이는 멀티스레딩이나 멀티프로세싱과 다릅니다. 비동기 프로그래밍은 한 번에 하나의 작업만 실행하지만, 특정 작업이 완료될 때까지 기다리는 동안 다른 작업으로 전환할 수 있습니다.

  • 동기 프로그래밍: 작업이 순차적으로 실행. 하나의 작업이 끝날 때까지 다른 작업 대기.
  • 비동기 프로그래밍: 작업이 블로킹되지 않고, 작업 중간에 다른 작업이 실행될 수 있음.

2. asyncio란?

asyncio는 파이썬 3.3에 도입된 라이브러리로, 비동기 I/O 및 동시성 작업을 관리하는 데 사용됩니다. 주요 특징은 다음과 같습니다:

  • 이벤트 루프 기반으로 작동
  • 비동기 코루틴(async def)과 태스크(Task) 관리
  • 네트워크, 파일 I/O, 타이머 작업 처리

3. 핵심 구성 요소

1) 코루틴(Coroutine)

  • async def로 정의된 함수는 코루틴입니다.
  • 코루틴은 실행을 일시 중단하고 이벤트 루프에 제어권을 반환할 수 있습니다.
  • async def example_coroutine(): print("Start coroutine") await asyncio.sleep(1) # 실행을 일시 중단 print("End coroutine")

2) await

  • await는 다른 비동기 함수를 호출할 때 사용됩니다.
  • await은 제어권을 이벤트 루프에 넘기고, 호출된 작업이 완료되길 기다립니다.
  • async def main(): print("Start") await asyncio.sleep(2) # 2초 대기 print("End")

3) 이벤트 루프(Event Loop)

  • 이벤트 루프는 코루틴을 실행하고, 태스크를 스케줄링하며, 대기 중인 작업을 관리합니다.
  • asyncio.run()은 이벤트 루프를 생성하고 실행합니다.
  • import asyncio async def main(): print("Hello") await asyncio.sleep(1) print("World") asyncio.run(main()) # 이벤트 루프 실행

4) 태스크(Task)

  • 태스크는 코루틴의 실행을 관리하는 객체입니다.
  • 여러 코루틴을 동시에 실행하려면 asyncio.create_task()를 사용합니다.
  • async def task1(): await asyncio.sleep(1) print("Task 1 complete") async def task2(): await asyncio.sleep(2) print("Task 2 complete") async def main(): t1 = asyncio.create_task(task1()) t2 = asyncio.create_task(task2()) await t1 # 태스크 1 대기 await t2 # 태스크 2 대기 asyncio.run(main())

5) Future와 Task

  • Future는 비동기 작업의 결과를 나타내는 객체입니다.
  • Task는 코루틴을 실행하는 래퍼로, 내부적으로 Future를 사용합니다.

4. 주요 함수 및 메서드

1) asyncio.run(coroutine)

  • 비동기 프로그램의 시작점. 이벤트 루프를 실행.
  • 종료 시 루프를 닫음.

2) asyncio.create_task(coroutine)

  • 코루틴을 태스크로 변환하여 이벤트 루프에 등록.

3) asyncio.gather(*coroutines)

  • 여러 코루틴을 동시에 실행하고 결과를 반환.
  • async def main(): results = await asyncio.gather(task1(), task2()) print(results)

4) asyncio.sleep(seconds)

  • 비동기적으로 대기.

5) asyncio.TimeoutError

  • 일정 시간 내 작업이 완료되지 않을 경우 발생하는 예외.

5. 비동기 프로그래밍의 장점

  • 효율적인 자원 사용: 비동기 작업 중에 CPU를 활용할 수 있음.
  • 높은 처리량: I/O 바운드 작업을 병렬적으로 처리 가능.
  • 간결한 코드: 콜백 기반의 비동기 작업보다 이해하기 쉬움.

6. 실전 예제: 웹 요청 처리

다음은 aiohttp를 사용하여 여러 웹 페이지를 동시에 요청하는 예제입니다.

import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    urls = ["https://example.com", "https://python.org", "https://github.com"]
    tasks = [asyncio.create_task(fetch(url)) for url in urls]
    results = await asyncio.gather(*tasks)
    print(results)

asyncio.run(main())

7. 주의 사항

  1. I/O 바운드 작업에서만 유리하며, CPU 바운드 작업에는 비효율적일 수 있음.
  2. 이벤트 루프는 단일 스레드에서 실행되므로, 멀티스레딩/멀티프로세싱과는 다름.
  3. 동기 코드와 혼합하면 프로그램의 성능이 저하될 수 있음.

asyncio는 비동기 프로그래밍의 복잡성을 크게 줄여주는 강력한 도구입니다. 네트워크 요청, 파일 입출력, 타이머와 같은 작업에서 효율성을 높이는 데 매우 유용하니, 실제 사용 사례를 기반으로 익혀보는 것을 추천합니다.

 

 

반응형

댓글