본문으로 바로가기

[Python][기본 03] async/await

category Launguage/Python 2023. 2. 8. 18:53

Python 에서도 JavaScript와 같이 비동기로 동작하는 코드를 짤 수 있을까?

그렇다. `asyncio` 를 사용하면 가능하다!

 

동시 프로그래밍 패러다임의 변화

전통적으로 동시 프로그래밍 (concurrent programming)은 여러 개의 쓰레드(thread)를 활용하여 이루어졌다.

하지만 쓰레드를 이용해서 직접 코드를 작성해 보았다면 thread safe 한 코드를 작성하는 것은 생각보다 쉬운 일이 아니다.

게다가 싱글 코어 프로세서에서 이런 프로그램을 돌리면 동시 처리에 따른 성능 향상이 기대했던 것 보다 미미하거나 저하되는 경우가 빈번하다.

이러한 이유로 언젠가부터 하나의 쓰레드로 동시 처리를 하는 비동기 프로그래밍 (asynchronous programming)이 더욱 주목받기 시작했다.

 

비동기 프로그래밍(asynchronous programming)

웹 서버와 같은 어플리케이션을 생각해보면 CPU 연산 시간 대비 DB 또는 API를 연동하는 과정에서 발생하는 대기 시간이 훨씬 길다는 것을 알 수 있다. 비동기 프로그래밍은 이러한 대기 시간을 낭비하지 않고 그 시간에 CPU가 다른 처리를 할 수 있도록 하는데 이를 흔히 non-blocking 이라 표현한다.

JavaScript와 같이 애초에 비동기 방식으로 동작하도록 설계된 언어에서는 익숙한 개념이지만, 파이썬과 같이 기본적으로 동기 방식으로 동작하는 언어에서는 이 개념이 생소하게 느껴질 수도 있다. 하지만 Python 3.4에서 asyncio가 표준 라이브러리로 추가되었고, Python 3.5 에서 async/await 키워드가 문법으로 채택이 되면서, 파이썬도 이제 언어 자체적으로 비동기 프로그래밍이 가능해졌다.

 

핵심 문법

def 키워드로 선언하는 모든 함수는 파이썬에서 동기식으로 동작한다고 생각하면 되겠다.

# 동기 함수
def do_sync():
    pass

기존 def 키워드 앞에 async 라는 키워드가 붙었다면 이 함수는 비동기 처리되며, 이러한 비동기 함수를 파이썬에서는 코루틴(coroutine)이라고 표현한다.

# 비동기 함수
async def do_async():
    pass

이러한 비동기 함수는 동기 함수가 호출하듯이 호출하면 coroutine 객체가 리턴된다.

do_async() # <coroutine object do_async at 0x1038de710>

따라서 비동기 함수는 일반적으로 async 로 선언된 다른 비동기 함수 내에서 await 키워드를 붙여서 호출해야 한다.

async def main_async():
    await do_async()
JavaScript 에서 async로 선언된 다른 비동기 함수를 호출할 때 await 키워드를 붙이지 않으면 Promise 객체를 리턴하는 것과 같다.

async 로 선언되지 않은 동기 함수 내에서 비동기 함수를 호출하려면 asyncio 라이브러리의 이벤트 루프를 이용해야 한다.

loop = asyncio.get_event_loop()
loop.run_until_complete(main_async())
loop.close()

# Python 3.7 이상의 버전에서는 아래와 같이 한줄로 간단히 호출도 가능하다.
asyncio.run(main_async())